feat: 商品联调

This commit is contained in:
zc 2025-10-23 11:11:03 +08:00
parent 3ff3977ed0
commit 5561b8d4bb
18 changed files with 728 additions and 83 deletions

View File

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>DC信贷系统</title>
<title></title>
</head>
<body>
<div id="app"></div>

View File

@ -10,6 +10,12 @@ const login = {
getCategoryList: ['/category/list'], // 获取类目列表
sortCategory: ['/category/sort'], // 排序
updateCategory: ['/category/insertOrUpdate'], // 插入或更新
/**
* app类目管理
*/
getAppCategoryList: ['/app/category/list'], // 获取类目列表
sortAppCategory: ['/app/category/sort'], // 排序
updateAppCategory: ['/app/category/insertOrUpdate'], // 插入或更新
/**
*

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

@ -0,0 +1,294 @@
<template>
<el-dialog v-model="dialogVisible" title="选择资源" width="70%" :before-close="handleClose">
<div class="file-explorer">
<!-- 面包屑导航 -->
<div class="breadcrumb">
<el-breadcrumb separator="/">
<el-breadcrumb-item
v-for="(path, index) in currentPathArray"
:key="index"
:class="{ 'is-link': index < currentPathArray.length - 1 }"
@click="navigateTo(index)"
>
{{ path.name }}
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<!-- 文件网格视图 -->
<div class="file-list">
<div class="grid-container" v-loading="loading">
<div
v-for="item in fileList"
:key="item.id"
class="file-grid-item"
:class="{ 'is-selected': item.type === 'file' && selectedFilesMap[item.id] }"
@click="handleItemClick(item)"
@dblclick="handleItemDblClick(item)"
>
<!-- 文件夹图标 -->
<div class="file-icon" v-if="item.type === 'file'">
<el-icon class="directory-icon"><Folder /></el-icon>
</div>
<!-- 文件图标或缩略图 -->
<div class="file-icon" v-else>
<!-- 图片文件显示缩略图 -->
<img v-if="item.type === 'image'" :src="item.resourceUrl" class="thumbnail" />
<video v-if="item.type === 'video'" :src="item.resourceUrl" class="thumbnail"></video>
<!-- 文件选择复选框 -->
<el-checkbox
v-if="['image', 'video'].includes(item.type)"
v-model="selectedFilesMap[item.id]"
@click.stop
/>
</div>
<!-- 文件名称 -->
<div class="file-name" :title="item.fileName">{{ item.fileName }}</div>
<!-- 文件大小 -->
<!-- <div class="file-size" v-if="showSize && item.type === 'file'">
{{ formatFileSize(item.size) }}
</div> -->
</div>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button
class="!text-white"
type="primary"
@click="confirmSelection"
:disabled="selectedFiles.length === 0"
>
确认选择 ({{ selectedFiles.length }})
</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ElMessage } from 'element-plus'
import { Folder } from '@element-plus/icons-vue'
interface FileItem {
id: number
fileName: string
type: 'file' | 'image' | 'video'
resourceUrl?: string
}
const model = defineModel<boolean>()
const initPathArray = defineModel<{ name: string; id: number }[]>('initPathArray')
const emit = defineEmits<{
(e: 'update:modelValue', value: boolean): void
(e: 'select', files: FileItem[]): void
}>()
//
const dialogVisible = computed({
get: () => model.value,
set: (value) => (model.value = value)
})
const currentFileId = ref<number>(0) // id
const currentPathArray = ref<{ name: string; id: number }[]>([]) //
//
const fileList = ref<FileItem[]>([])
const loading = ref(false)
//
const selectedFilesMap = ref<Record<number, boolean>>({})
const selectedFiles = ref<FileItem[]>([])
//
const loadFileList = async (parentId = 0) => {
loading.value = true
const result = await api.resource.getResourceList.post!<any>({
parentId: parentId
}).finally(() => (loading.value = false))
fileList.value = result.data.rows
//
selectedFilesMap.value = {}
selectedFiles.value = []
}
//
const navigateTo = (index: number) => {
if (index >= currentPathArray.value.length - 1) return
currentFileId.value = (currentPathArray.value[index] as any).id
currentPathArray.value = currentPathArray.value.slice(0, index + 1)
loadFileList(currentFileId.value)
}
//
const handleItemClick = (item: FileItem) => {
if (['image', 'video'].includes(item.type)) {
selectedFilesMap.value[item.id] = !selectedFilesMap.value[item.id]
if (selectedFilesMap.value[item.id]) {
selectedFiles.value.push(item)
} else {
selectedFiles.value = selectedFiles.value.filter((f) => f.id !== item.id)
}
}
}
//
const handleItemDblClick = (item: FileItem) => {
if (item.type === 'file') {
currentFileId.value = item.id
currentPathArray.value.push({ id: item.id, name: item.fileName })
loadFileList(item.id)
}
}
//
const confirmSelection = () => {
if (selectedFiles.value.length === 0) {
return ElMessage.warning('请至少选择一个文件')
}
emit('select', selectedFiles.value)
dialogVisible.value = false
}
//
const handleClose = () => {
dialogVisible.value = false
}
/*
//
const formatFileSize = (size: number) => {
if (!size) return '0 B'
if (size < 1024) return `${size} B`
if (size < 1024 * 1024) return `${(size / 1024).toFixed(2)} KB`
if (size < 1024 * 1024 * 1024) return `${(size / 1024 / 1024).toFixed(2)} MB`
return `${(size / 1024 / 1024 / 1024).toFixed(2)} GB`
} */
//
watch(
() => dialogVisible.value,
(val) => {
if (val) {
currentPathArray.value = initPathArray.value || []
currentFileId.value = initPathArray.value![initPathArray.value!.length - 1].id
loadFileList(currentFileId.value)
}
},
{ immediate: true }
)
</script>
<style scoped>
.file-explorer {
height: 60vh;
display: flex;
flex-direction: column;
}
.breadcrumb {
padding: 8px 0;
}
.file-list {
flex: 1;
overflow: auto;
padding: 10px;
}
.grid-container {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.file-grid-item {
width: 120px;
height: 150px;
border-radius: 4px;
padding: 10px;
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
transition: background-color 0.2s;
position: relative;
}
.file-grid-item:hover {
background-color: #f5f7fa;
}
.file-grid-item.is-selected {
background-color: #ecf5ff;
}
.file-icon {
height: 120px;
width: 120px;
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.directory-icon {
font-size: 100px;
color: #909399;
}
.file-type-icon {
font-size: 50px;
color: #909399;
}
.thumbnail {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: 4px;
}
.image-placeholder {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f7fa;
border-radius: 4px;
font-size: 30px;
color: #909399;
}
.file-name {
width: 100%;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 14px;
}
.file-size {
font-size: 12px;
color: #909399;
margin-top: 4px;
}
:deep(.is-link) {
cursor: pointer;
color: var(--el-color-primary);
}
:deep(.el-checkbox) {
position: absolute;
top: -5px;
left: 5px;
z-index: 1;
}
</style>

View File

@ -0,0 +1,65 @@
<template>
<div class="resource-upload">
<div class="resource-upload__wrapper">
<el-button type="primary" :size="size" class="relative !text-white">
{{ buttonText }}
<input
ref="inputRef"
class="!w-20 !h-8 absolute top-0 left-0 cursor-pointer opacity-0"
type="file"
:multiple="multiple"
:accept="accept"
@change="onFilesChange"
/>
</el-button>
</div>
</div>
</template>
<script setup lang="ts">
interface Props {
/** 是否允许多选文件 */
multiple?: boolean
size?: 'small' | 'normal'
/** 接受的文件类型 */
accept?: 'image/*,video/*' | 'image/*' | 'video/*'
/** 按钮文本 */
buttonText?: string
}
withDefaults(defineProps<Props>(), {
multiple: false,
accept: 'image/*,video/*',
buttonText: '选择文件',
size: 'normal'
})
const emit = defineEmits<{
(e: 'change', files: File[]): void
}>()
const inputRef = ref<HTMLInputElement | null>(null)
const onFilesChange = async (e: Event) => {
const input = e.target as HTMLInputElement
const files = Array.from(input.files || [])
if (!files.length) return
// reset input so selecting the same files again will still trigger change
if (inputRef.value) inputRef.value.value = ''
const form = new FormData()
// 'files'
files.forEach((f) => {
form.append('files', f)
})
const res = await api.resource.uploadFile.post!<any>(form)
const result = res.data.map((item: any) => ({
fileName: item.srcFileName,
resourceUrl: item.url,
type: item.srcFileName.endsWith('.mp4') ? 'video' : 'image'
}))
emit('change', result)
}
</script>
<style scoped lang="scss"></style>

View File

@ -0,0 +1,125 @@
<template>
<div class="resource-upload">
<div v-if="fileList.length" class="image-list">
<div v-for="item in fileList" :key="item.resourceUrl" class="image-item">
<img
v-if="item.type === 'image'"
:src="item.resourceUrl"
class="image-item-media"
alt="preview"
@click="handlePictureCardPreview(item)"
/>
<video
v-else
:src="item.resourceUrl"
class="image-item-media"
@click="handlePictureCardPreview(item)"
/>
<div class="meta">
<span class="name" :title="item.name">{{ item.name }}</span>
<el-button link type="danger" size="small" @click="remove(item.resourceUrl)"
>移除</el-button
>
</div>
</div>
</div>
<el-dialog v-model="showReviewImgDialog" @close="onDialogClose">
<img
w-full
v-if="reviewImg.type === 'image'"
:src="reviewImg.resourceUrl"
alt="Preview Image"
/>
<video v-else ref="reviewVideoRef" :src="reviewImg.resourceUrl" autoplay />
</el-dialog>
</div>
</template>
<script setup lang="ts">
interface PreviewItem {
fileName: string
name: string
resourceUrl: string
type: 'image' | 'video'
}
const emit = defineEmits<{
(e: 'remove', file: PreviewItem): void
}>()
const fileList = defineModel<PreviewItem[]>('fileList', {
type: Array as PropType<PreviewItem[]>,
default: () => []
})
const remove = (url: string) => {
const idx = fileList.value.findIndex((x) => x.resourceUrl === url)
if (idx === -1) return
const [removed] = fileList.value.splice(idx, 1)
emit('remove', removed)
}
const showReviewImgDialog = ref(false)
const reviewImg = ref<PreviewItem>({})
const handlePictureCardPreview = (item: PreviewItem) => {
reviewImg.value = item
showReviewImgDialog.value = true
}
const reviewVideoRef = ref<HTMLVideoElement | null>(null)
const onDialogClose = () => {
const video = reviewVideoRef.value
if (video) {
try {
video.pause()
video.currentTime = 0
} catch (e) {
console.error(e)
}
}
}
</script>
<style scoped lang="scss">
.resource-upload {
.image-list {
margin-top: 12px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 12px;
}
.image-item {
border: 1px solid #ebeef5;
border-radius: 6px;
background: #fff;
padding: 8px;
display: flex;
flex-direction: column;
gap: 6px;
.image-item-media {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 4px;
background: #f5f7fa;
}
.meta {
display: flex;
align-items: center;
gap: 8px;
.name {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
color: #606266;
}
}
}
}
</style>

View File

@ -1,11 +1,11 @@
<template>
<div class="resource-upload">
<div class="resource-upload__wrapper">
<el-button type="primary" class="relative !text-white">
<el-button type="primary" size="small" class="relative !text-white">
选择文件
<input
ref="inputRef"
class="absolute top-0 left-0 cursor-pointer opacity-0"
class="!w-20 absolute top-0 left-0 cursor-pointer opacity-0"
type="file"
multiple
accept="image/*,video/*"

View File

@ -43,7 +43,12 @@ const handleLogout = () => {
<template>
<div class="navbar">
<div class="left-menu">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<hamburger
id="hamburger-container"
:is-active="sidebar.opened"
class="hamburger-container"
@toggleClick="toggleSideBar"
/>
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
</div>
<div class="right-menu">
@ -74,7 +79,7 @@ const handleLogout = () => {
justify-content: space-between;
.left-menu {
display: flex;
.hamburger-container {
.hamburger-container {
line-height: 46px;
height: 100%;
float: left;

View File

@ -5,8 +5,8 @@
<img v-if="logo" :src="logo" class="sidebar-logo" />
</router-link>
<router-link v-else key="expand" class="sidebar-logo-link" to="/home">
<img v-if="logo" :src="logo" class="sidebar-logo" style="margin-left: -60px;" />
<h1 class="sidebar-title">DC信贷系统</h1>
<img v-if="logo" :src="logo" class="sidebar-logo" style="margin-left: -60px" />
<h1 class="sidebar-title">购得着后台</h1>
</router-link>
</transition>
</div>

View File

@ -49,7 +49,7 @@ const classObj = computed(() => ({
const { user } = useStore()
const phone = user.userPhone.slice(-4)
const watermark = new Watermark({
content: `DC信贷系统 ${user.userNickName}_${phone}`,
content: `购得着后台 ${user.userNickName}_${phone}`,
tip: `${handleData.formatTime(new Date(), 'YYYY-MM-DD hh:mm:ss')}`,
alpha: '0.2',
rotate: 340,

View File

@ -12,14 +12,10 @@
:load="handleLoadNode"
lazy
highlight-current
@node-click="onClick"
:expand-on-click-node="false"
@node-click="handleNodeClick"
@node-drag-start="handleDragStart"
@node-drag-enter="handleDragEnter"
@node-drag-leave="handleDragLeave"
@node-drag-over="handleDragOver"
@node-drag-end="handleDragEnd"
@node-drop="handleDrop"
@node-drop="handleDropFinish"
>
<template #default="{ node, data }">
<div class="category-tree-node">
@ -47,16 +43,9 @@
</template>
<script lang="ts" setup>
import { ElButton } from 'element-plus'
import { ElButton, ElMessage } from 'element-plus'
import type { RenderContentContext } from 'element-plus'
import {
handleDragStart,
handleDragEnter,
handleDragOver,
handleDragLeave,
handleDragEnd,
handleDrop
} from './use-drag'
import { handleDragStart, handleDropFinish } from './use-drag'
interface Tree {
id: number
@ -68,7 +57,6 @@ type Data = RenderContentContext['data']
const curId = ref(NaN)
const curParentId = ref(NaN)
// const curId = ref(NaN)
const inputRef = ref()
const treeRef = ref()
@ -82,12 +70,17 @@ const onEdit = (data: Data) => {
}
//
const handleInputConfirm = (data: any) => {
const handleInputConfirm = async (data: any) => {
if (curId.value === Infinity) {
api.commodity.updateCategory.post!({ ...data, name: data.label2, parentId: curParentId.value })
await api.commodity.updateCategory.post!({
...data,
name: data.label2,
parentId: curParentId.value
})
} else {
api.commodity.updateCategory.post!({ ...data, name: data.label2 })
await api.commodity.updateCategory.post!({ ...data, name: data.label2 })
}
ElMessage.success('更新成功')
data.name = data.label2
data.label2 = ''
curId.value = NaN
@ -156,8 +149,9 @@ const handleLoadNode = (node, resolve) => {
})
}
const onClick = (data) => {
console.warn('----- my data is data111: ', data)
//
const handleNodeClick = (node: Node, data: Data) => {
console.log('click', node, data)
}
</script>

View File

@ -1,39 +1,24 @@
import type { DragEvents } from 'element-plus/es/components/tree/src/model/useDragNode'
import type { NodeDropType, RenderContentContext } from 'element-plus'
type Node = RenderContentContext['node']
// 开始拖拽
export const handleDragStart = (node: Node, ev: DragEvents) => {
export const handleDragStart = (node: Node) => {
console.log('drag start', node)
}
// 拖拽进入其他节点时触发的事件
export const handleDragEnter = (draggingNode: Node, dropNode: Node, ev: DragEvents) => {
console.log('tree drag enter:', dropNode.label)
}
// 拖拽离开某个节点时触发的事件
export const handleDragLeave = (draggingNode: Node, dropNode: Node, ev: DragEvents) => {
console.log('tree drag leave:', dropNode.label)
}
// 在拖拽节点时触发的事件(类似浏览器的 mouseover 事件)
export const handleDragOver = (draggingNode: Node, dropNode: Node, ev: DragEvents) => {
console.log('tree drag over:', dropNode.label)
}
// 拖拽结束时(可能未成功)触发的事件
export const handleDragEnd = (
draggingNode: Node,
dropNode: Node,
dropType: NodeDropType,
ev: DragEvents
) => {
console.log('tree drag end:', dropNode && dropNode.label, dropType)
}
// 拖拽成功完成时触发的事件
export const handleDrop = (
draggingNode: Node,
dropNode: Node,
dropType: NodeDropType,
ev: DragEvents
) => {
console.log('tree drop:', dropNode.label, dropType)
export const handleDropFinish = (draggingNode: Node, dropNode: Node, dropType: NodeDropType) => {
// 获取拖拽之后最新的ids
const getSortIds = () => {
const parentNode = dropType === 'inner' ? dropNode : dropNode.parent // 获取父节点
if (!parentNode || !parentNode.childNodes) return []
return parentNode.childNodes.map((node) => node.data.id)
}
// 获取拖拽到的位置索引
const sortIds = getSortIds()
if (!sortIds.length) return
api.commodity.sortCategory.post!({ categoryIds: sortIds }).then(() => {
console.log('tree drop:', draggingNode, dropNode.label, dropType)
})
}

View File

@ -0,0 +1,48 @@
// 选择资源库图片
export const useImportFile = () => {
const showFileExplorer = ref(false)
const fileType = {
mainImageUrl: '主图',
videoUrl: '视频',
subImageUrl: '副图'
}
const curFileType = ref<'mainImageUrl' | 'videoUrl' | 'subImage'>('mainImageUrl')
const currentPathArray = ref<{ name: string; id: number }[]>([{ name: '根目录', id: 0 }])
// 处理文件选择
const handleChooseResourceFileCallback = (files: any[] = []) => {
handleChooseFiles(curFileType.value, files)
}
const onClickChooseResourceBtn = (type: 'mainImageUrl' | 'videoUrl' | 'subImage') => {
curFileType.value = type
showFileExplorer.value = true
}
return {
showFileExplorer,
currentPathArray,
handleChooseResourceFileCallback,
onClickChooseResourceBtn
}
}
export const fileList = ref<any[]>([])
export const handleChooseFiles = (
fileType: 'mainImageUrl' | 'videoUrl' | 'subImage',
files: any = []
) => {
const fileNameMap = {
mainImageUrl: '主图',
videoUrl: '视频',
subImage: '副图'
}
if (['mainImageUrl', 'videoUrl'].includes(fileType)) {
const index = fileList.value.findIndex((item: any) => item.fileType === fileType)
if (index !== -1) {
fileList.value.splice(index, 1)
}
}
fileList.value.push(
...files.map((item: any) => ({ ...item, fileType, name: fileNameMap[fileType] }))
)
}

View File

@ -4,11 +4,12 @@ export const initConfig = () => {
configData.value = pageConfig({
dialog: [
{
title: { label: '商品标题' },
comCategoryId: { label: 'app类目', slot: 'comCategoryId' },
mainImageUrl: { label: '主图', slot: 'file' },
videoUrl: { label: '视频', slot: 'file' },
subImageUrl: { label: '副图', slot: 'file' }
title: { label: '商品标题', class: '!w-full' },
appCategoryId: { label: 'app类目', slot: 'appCategoryId' },
mainImageUrl: { label: '主图', slot: 'mainFile' },
videoUrl: { label: '视频', slot: 'videoFile' },
subImageUrl: { label: '副图', slot: 'subFile' },
fileReviewList: { label: '图片预览', slot: 'fileReviewList' }
}
]
})

View File

@ -3,19 +3,75 @@
<el-card>
<h4 class="font-bold">基本信息</h4>
<form-wrap :form="$dialog">
<template #comCategoryId>
<template #appCategoryId>
<el-tree-select
v-model="$dialog.data.comCategoryId"
:data="categoryOptions"
></el-tree-select>
show-checkbox
v-model="$dialog.data.appCategoryId"
:props="defaultAdminCategoryTreeProps"
:render-after-expand="false"
@check="
(checkedNode: any, checkedStatus: any) =>
handleCheckChange(checkedNode, checkedStatus, 'app')
"
:load="handleLoadNode2"
lazy
>
</el-tree-select>
</template>
<template #file>
<resource-upload v-model="fileList" />
<template #mainFile>
<file-upload-btn
accept="image/*"
size="normal"
@change="(val) => handleChooseFiles('mainImageUrl', val)"
/>
<el-button class="ml-2" @click="onClickChooseResourceBtn('mainImageUrl')"
>资源库导入</el-button
>
</template>
<template #videoFile>
<file-upload-btn
size="normal"
accept="video/*"
@change="(val) => handleChooseFiles('videoUrl', val)"
/>
<el-button class="ml-2" @click="onClickChooseResourceBtn('videoUrl')"
>资源库导入</el-button
>
</template>
<template #subFile>
<file-upload-btn
size="normal"
multiple
accept="image/*"
@change="(val) => handleChooseFiles('subImage', val)"
/>
<el-button class="ml-2" @click="onClickChooseResourceBtn('subImage')"
>资源库导入</el-button
>
</template>
<template #fileReviewList>
<resource-review v-model:fileList="fileList" />
</template>
</form-wrap>
</el-card>
<el-card class="mt-2 sku-info">
<h4 class="font-bold mb-2">SKU信息:</h4>
<el-form-item label="后台类目">
<el-tree-select
show-checkbox
v-model="$dialog.data.categoryId"
:props="defaultAdminCategoryTreeProps"
:render-after-expand="false"
@check="
(checkedNode: any, checkedStatus: any) =>
handleCheckChange(checkedNode, checkedStatus, 'admin')
"
:load="handleLoadNode"
lazy
style="width: 240px"
>
</el-tree-select>
</el-form-item>
<div ref="dragRef">
<dl v-for="(item, index) in categoryData" :key="item.typeId" class="flex items-center">
<dt class="text-sm text-[#666] mb-2">{{ item.typeName }}</dt>
@ -60,26 +116,52 @@
<wan-editor v-model="htmlContent"></wan-editor>
</el-card>
<el-button type="primary" @click="onSubmit()">提交</el-button>
<FileExplorerDialog
v-model="showFileExplorer"
v-model:initPathArray="currentPathArray"
@select="handleChooseResourceFileCallback"
/>
</div>
</template>
<script setup lang="ts">
import { initConfig } from './config'
import { categoryDataMock, categoryOptionsMock } from './mock'
import { generateTargetData, useCategoryAddOption, colors, useDrag } from './use-method'
import {
generateTargetData,
useCategoryAddOption,
colors,
useDrag,
handleLoadNode,
handleLoadNode2,
handleCheckChange,
defaultAdminCategoryTreeProps,
checkedNodes
} from './use-method'
import categoryConfig from './category-config.vue'
import wanEditor from './editor.vue'
import resourceUpload from '@/components/ResourceUpload/index.vue'
import fileUploadBtn from '@/components/FileUploadBtn/index.vue'
import resourceReview from '@/components/ResourceReview/index.vue'
import { useImportFile, fileList, handleChooseFiles } from './choose-file-method'
import FileExplorerDialog from '@/components/FileExplorerDialog/index.vue'
const route = useRoute()
const init = async () => {
checkedNodes.value = { admin: [], app: [] }
await api.commodity.getCommodityDetail.post!({ productId: route.query.id })
}
const { $dialog, dialog1 } = toRefs(initConfig().value!)
$dialog.value.config = dialog1.value
$dialog.value['item-width'] = '100%'
const categoryOptions = ref(categoryOptionsMock)
const {
showFileExplorer,
currentPathArray,
handleChooseResourceFileCallback,
onClickChooseResourceBtn
} = useImportFile()
const categoryOptions123 = ref(categoryOptionsMock)
const categoryData = ref(categoryDataMock)
const { inputValue, InputRefs, inputVisibles, onAddCategoryDataOption, handleInputConfirm } =
useCategoryAddOption(categoryData)
@ -91,12 +173,6 @@ console.warn('----- my data is list.value: ', list.value)
const htmlContent = ref('自定义')
const fileList = ref([])
const onGetResource = (v) => {
console.warn('----- my data is v: ', v)
}
const onSubmit = () => {
console.warn('----- my data is htmlContent: ', htmlContent.value)
}

View File

@ -95,3 +95,49 @@ export const useDrag = (list: any) => {
}
return { dragRef, createDrag }
}
export const defaultAdminCategoryTreeProps = {
label: 'label',
children: 'children',
disabled: 'disabled',
isLeaf: (data: any) => {
return !data.hasChild
}
}
// 树结构懒加载后台类目
export const handleLoadNode = (node: any, resolve: any) => {
api.commodity.getCategoryList.post!<any>({ parentId: node.data.value || 0 }).then((res) => {
resolve(
res.data.map((item: any) => ({
label: item.name,
value: item.id,
hasChild: item.hasChild,
disabled: item.hasChild,
categoryPropertyList: item.vvCategoryPropertyDTOList
}))
)
})
}
// 树结构懒加载后台类目
export const handleLoadNode2 = (node: any, resolve: any) => {
api.commodity.getAppCategoryList.post!<any>({ parentId: node.data.value || 0 }).then((res) => {
resolve(
res.data.map((item: any) => ({
label: item.name,
value: item.id,
hasChild: item.hasChild,
disabled: item.hasChild,
categoryPropertyList: item.vvCategoryPropertyDTOList
}))
)
})
}
export const checkedNodes = ref<{ admin: number[]; app: number[] }>({ admin: [], app: [] })
export const handleCheckChange = (_: any, checkedStatus: any, type: 'admin' | 'app') => {
// checkedStatus: { checkedKeys, checkedNodes, halfCheckedKeys, halfCheckedNodes }
checkedNodes.value[type] = [...checkedStatus.halfCheckedKeys, ...checkedStatus.checkedKeys]
console.warn('----- my data is checkedNodes: ', checkedNodes)
}

View File

@ -1,7 +1,7 @@
<template>
<div id="home">
<img src="~@/assets/images/home_bg.jpg" />
<p>欢迎来到DC信贷系统</p>
<p>欢迎来到购得着后台</p>
</div>
</template>

View File

@ -94,7 +94,7 @@ const handleLogin = () => {
// width: 420px;
// height: 430px;
// background-color: #ecf5ff;
border-radius: 30px;
border-radius: 10px;
text-align: center;
padding: 10px 20px;
position: absolute;
@ -130,7 +130,7 @@ const handleLogin = () => {
}
}
.login_submit_btn {
margin: 30px 0;
margin: 30px 0 0;
.el-button {
width: 100%;
height: 40px;