feat: cache
This commit is contained in:
parent
af14f26096
commit
d32d264066
278
src/views/login/index.vue
Normal file
278
src/views/login/index.vue
Normal file
@ -0,0 +1,278 @@
|
||||
<script setup>
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { Encrypt } from '@/utils/crypto'
|
||||
import { getCurrentTime } from '@/utils/config'
|
||||
import useStore from '@/stores'
|
||||
import { apiGetImgCode, apiGetSmsCode } from '@/api/login'
|
||||
import DialogPassword from '@/components/DialogPassword/index.vue'
|
||||
|
||||
const { user } = useStore()
|
||||
const router = useRouter()
|
||||
|
||||
// 绑定el-form的ref属性
|
||||
const loginFormRef = ref(null)
|
||||
const loginData = reactive({
|
||||
userPhoneEn: '',
|
||||
password: '',
|
||||
imageCode: '',
|
||||
uuid: '',
|
||||
smsCode: ''
|
||||
})
|
||||
const loginRules = reactive({
|
||||
userPhoneEn: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{
|
||||
pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
|
||||
message: '请输入正确的手机号码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
imageCode: [
|
||||
{ required: true, message: '请输入图像验证码', trigger: 'blur' },
|
||||
{ min: 4, message: '图像验证码不能少于4位', trigger: 'blur' }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 8, message: '密码不能少于8位', trigger: 'blur' },
|
||||
{ max: 16, message: '密码不能超过16位', trigger: 'blur' }
|
||||
],
|
||||
smsCode: [
|
||||
{ required: true, message: '请输入短信验证码', trigger: 'blur' },
|
||||
{ min: 6, message: '短信验证码不能少于6位', trigger: 'blur' }
|
||||
]
|
||||
})
|
||||
const imageCodeUrl = ref('') // 图像验证码
|
||||
const hasSendMsg = ref(false) // 是否发送过短信
|
||||
const time = ref(0) // 倒计时
|
||||
const dialogPasswordVisible = ref(false)
|
||||
|
||||
// 获取图像验证码
|
||||
const handleGetImgCode = () => {
|
||||
apiGetImgCode().then((res) => {
|
||||
if (res.code === 200) {
|
||||
imageCodeUrl.value = `data:image/gif;base64,${res.data.img}`
|
||||
loginData.uuid = res.data.uuid
|
||||
}
|
||||
})
|
||||
}
|
||||
handleGetImgCode()
|
||||
|
||||
// 获取短信验证码
|
||||
const timer = ref()
|
||||
const handleGetSmsCode = () => {
|
||||
if (hasSendMsg.value) return
|
||||
const emptyData = {
|
||||
userPhoneEn: loginData.userPhoneEn,
|
||||
password: loginData.password,
|
||||
imageCode: loginData.imageCode,
|
||||
uuid: loginData.uuid
|
||||
}
|
||||
if (hasEmptyValue(emptyData)) {
|
||||
return ElMessage.error('请输入对应信息')
|
||||
}
|
||||
apiGetSmsCode({
|
||||
userPhoneEn: loginData.userPhoneEn,
|
||||
password: Encrypt(loginData.password),
|
||||
imageCode: loginData.imageCode,
|
||||
uuid: loginData.uuid
|
||||
}).then((res) => {
|
||||
if (res.code === 200) {
|
||||
hasSendMsg.value = true
|
||||
time.value = 60
|
||||
timeCb()
|
||||
} else {
|
||||
ElMessage.error(res.msg)
|
||||
setTimeout(() => {
|
||||
handleGetImgCode()
|
||||
loginData.imageCode = ''
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
|
||||
// 倒计时
|
||||
const timeCb = () => {
|
||||
clearTimeout(timer)
|
||||
time.value--
|
||||
if (time.value < 1) {
|
||||
return (hasSendMsg.value = false)
|
||||
}
|
||||
timer.value = setTimeout(timeCb, 1000)
|
||||
}
|
||||
}
|
||||
|
||||
// 登录
|
||||
const handleLogin = () => {
|
||||
loginFormRef.value.validate((valid) => {
|
||||
if (valid) {
|
||||
user.onLogin(loginData).then((res) => {
|
||||
if (res.code === 200) {
|
||||
router.push({ path: '/home' })
|
||||
} else if (res.code === 6005) {
|
||||
dialogPasswordVisible.value = true
|
||||
}
|
||||
})
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 是否有为空的值
|
||||
const hasEmptyValue = (obj) => {
|
||||
for (let key in obj) {
|
||||
if (obj[key] == null || obj[key] === '') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 获取当前时间
|
||||
const currentDate = ref()
|
||||
setInterval(() => {
|
||||
currentDate.value = getCurrentTime()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="login">
|
||||
<el-card class="login_elCard">
|
||||
<h2>东成工作台</h2>
|
||||
<h3>{{ currentDate }}</h3>
|
||||
<el-form
|
||||
:model="loginData"
|
||||
:rules="loginRules"
|
||||
label-width="auto"
|
||||
ref="loginFormRef"
|
||||
@keyup.enter="handleLogin"
|
||||
class="login_form"
|
||||
>
|
||||
<el-form-item label="手机号码" prop="userPhoneEn">
|
||||
<el-input
|
||||
v-model="loginData.userPhoneEn"
|
||||
placeholder="请输入手机号码"
|
||||
type="text"
|
||||
maxlength="11"
|
||||
class="login_form__input"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="登录密码" prop="password">
|
||||
<el-input
|
||||
v-model="loginData.password"
|
||||
placeholder="请输入登录密码"
|
||||
type="password"
|
||||
maxlength="16"
|
||||
minlength="8"
|
||||
show-password
|
||||
class="login_form__input"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="图像验证码" prop="imageCode">
|
||||
<el-input
|
||||
v-model="loginData.imageCode"
|
||||
placeholder="请输入图像验证码"
|
||||
type="text"
|
||||
maxlength="4"
|
||||
class="login_form__input login_form__img-code"
|
||||
>
|
||||
<template #append>
|
||||
<img class="login_form__input-img" @click="handleGetImgCode" :src="imageCodeUrl" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="短信验证码" prop="smsCode">
|
||||
<el-input
|
||||
v-model="loginData.smsCode"
|
||||
placeholder="请输入短信验证码"
|
||||
type="text"
|
||||
maxlength="6"
|
||||
class="login_form__input"
|
||||
>
|
||||
<template #append>
|
||||
<span class="cursor-pointer" @click="handleGetSmsCode">
|
||||
{{ hasSendMsg ? time + 's后重发' : '发送验证码' }}
|
||||
</span>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="login_submit_btn">
|
||||
<el-button @click="handleLogin" type="primary">登录</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<dialog-password
|
||||
ref="dialogPasswordRef"
|
||||
v-model:dialogPasswordVisible="dialogPasswordVisible"
|
||||
:loginData="loginData"
|
||||
:type="'init'"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#login {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('@/assets/images/login_bg1.png');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
position: relative;
|
||||
.login_tips {
|
||||
position: absolute;
|
||||
top: 20%;
|
||||
left: 20%;
|
||||
h3,
|
||||
p {
|
||||
color: #5d5d5e;
|
||||
}
|
||||
}
|
||||
.login_elCard {
|
||||
// width: 420px;
|
||||
// height: 430px;
|
||||
// background-color: #ecf5ff;
|
||||
border-radius: 30px;
|
||||
text-align: center;
|
||||
padding: 10px 20px;
|
||||
position: absolute;
|
||||
top: 20%;
|
||||
right: 10%;
|
||||
}
|
||||
.login_form {
|
||||
&__input {
|
||||
::v-deep(.el-input__inner) {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
&__img-code {
|
||||
.el-input__wrapper {
|
||||
padding-right: 1px;
|
||||
}
|
||||
::v-deep(.el-input-group__append) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
&__input-img {
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.el-input-group__append {
|
||||
cursor: pointer;
|
||||
}
|
||||
::v-deep(.el-form-item__label) {
|
||||
height: 40px !important;
|
||||
line-height: 40px !important;
|
||||
}
|
||||
}
|
||||
.login_submit_btn {
|
||||
margin: 30px 0;
|
||||
.el-button {
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
21
src/views/protocol/config/config.ts
Normal file
21
src/views/protocol/config/config.ts
Normal file
@ -0,0 +1,21 @@
|
||||
const configData = ref()
|
||||
export const initConfig = () => {
|
||||
configData.value = pageConfig({
|
||||
search: {
|
||||
comTitle: { label: '标题', clearable: true },
|
||||
comCategoryId: { label: '类目', option: [], clearable: true }
|
||||
},
|
||||
table: {
|
||||
id: { label: '产品ID' },
|
||||
comTitle: { label: '标题' },
|
||||
sales: { label: '销量' },
|
||||
price: { label: '价格' },
|
||||
btn: {
|
||||
types: ['primary', 'info', 'warning', 'success', 'danger'],
|
||||
names: ['编辑', '复制', '加入首页', '上下架', '删除'],
|
||||
width: 230
|
||||
}
|
||||
}
|
||||
})
|
||||
return configData
|
||||
}
|
||||
53
src/views/protocol/config/index.vue
Normal file
53
src/views/protocol/config/index.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<search-module :search="search" :table="table">
|
||||
<template #btn>
|
||||
<el-button @click="onAddOrEdit('add', null)" type="success">新增</el-button>
|
||||
</template>
|
||||
</search-module>
|
||||
<div id="table-drag-wrap">
|
||||
<table-module :table="table"> </table-module>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useDraggable } from 'vue-draggable-plus'
|
||||
import { initConfig } from './config'
|
||||
|
||||
/** 新增&编辑 */
|
||||
const onAddOrEdit = (type: string, row: any) => {}
|
||||
|
||||
/** 复制 */
|
||||
const onCopy = (row: any) => {}
|
||||
|
||||
// 加入首页
|
||||
const onAddHome = (row: any) => {}
|
||||
|
||||
// 删除
|
||||
const onDelete = (row: any) => {
|
||||
handleMessageBox({
|
||||
msg: `是否确认删除吗?`,
|
||||
success: api.commodity.delCommodity.post!,
|
||||
data: { id: row.id }
|
||||
}).then(() => {
|
||||
table.value.$onGetData(table.value)
|
||||
})
|
||||
}
|
||||
|
||||
/** 初始化页面 */
|
||||
const ConfigData = initConfig()
|
||||
const { search, table } = handleInit(ConfigData, api.commodity.getCommodityList.post, [
|
||||
onAddOrEdit.bind(null, 'edit'),
|
||||
onCopy,
|
||||
onAddHome,
|
||||
onDelete
|
||||
])
|
||||
table.value.$onGetData(table.value, 1, search)
|
||||
setTimeout(() => {
|
||||
useDraggable('#table-drag-wrap .el-table__body tbody', table.value.$data)
|
||||
}, 2000)
|
||||
|
||||
/** 页面缓存 */
|
||||
defineOptions({ name: 'supplier-config' })
|
||||
</script>
|
||||
@ -1,6 +1,8 @@
|
||||
type TCommodityType = 'online' | 'quit' | 'edit' | 'delete'
|
||||
|
||||
export const useCommodityType = (search: any, table: any) => {
|
||||
let beforeValue = 'online'
|
||||
const commodityType = ref('online')
|
||||
const commodityType = ref<TCommodityType>('online')
|
||||
const commodityTypeOptions = [
|
||||
{ label: '在线商品', value: 'online' },
|
||||
{ label: '下架商品', value: 'quit' },
|
||||
|
||||
128
src/views/supplier/xsSwitch/index.vue
Normal file
128
src/views/supplier/xsSwitch/index.vue
Normal file
@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<div id="category-tree" class="p-2 bg-white">
|
||||
<p>类目展示:</p>
|
||||
<el-tree
|
||||
style="max-width: 600px"
|
||||
:data="dataSource"
|
||||
node-key="id"
|
||||
draggable
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
@node-drag-start="handleDragStart"
|
||||
@node-drag-enter="handleDragEnter"
|
||||
@node-drag-leave="handleDragLeave"
|
||||
@node-drag-over="handleDragOver"
|
||||
@node-drag-end="handleDragEnd"
|
||||
@node-drop="handleDrop"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<div class="category-tree-node">
|
||||
<span v-if="curId !== data.id">{{ data.label }}</span>
|
||||
<el-input
|
||||
ref="inputRef"
|
||||
v-else
|
||||
v-model="data.label2"
|
||||
size="small"
|
||||
class="w-40"
|
||||
@keyup.enter="handleInputConfirm(data)"
|
||||
@blur="handleInputCancel(node, data)"
|
||||
/>
|
||||
<div>
|
||||
<el-button type="primary" link @click="edit(data)" size="small">编辑 </el-button>
|
||||
<el-button type="primary" link @click="append(data)" size="small"> 添加 </el-button>
|
||||
<el-button type="danger" size="small" link @click="remove(node, data)">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElButton } from 'element-plus'
|
||||
import type { RenderContentContext } from 'element-plus'
|
||||
import { dataSource } from './mock'
|
||||
import {
|
||||
handleDragStart,
|
||||
handleDragEnter,
|
||||
handleDragOver,
|
||||
handleDragLeave,
|
||||
handleDragEnd,
|
||||
handleDrop
|
||||
} from './use-drag'
|
||||
|
||||
interface Tree {
|
||||
id: number
|
||||
label: string
|
||||
children?: Tree[]
|
||||
}
|
||||
type Node = RenderContentContext['node']
|
||||
type Data = RenderContentContext['data']
|
||||
|
||||
const curId = ref(NaN)
|
||||
const inputRef = ref()
|
||||
|
||||
const edit = (data: Data) => {
|
||||
data.label2 = data.label
|
||||
curId.value = data.id
|
||||
nextTick(() => {
|
||||
inputRef.value.focus()
|
||||
})
|
||||
}
|
||||
|
||||
// 回车确认
|
||||
const handleInputConfirm = (data: any) => {
|
||||
if (curId.value === Infinity) {
|
||||
// 新增接口
|
||||
} else {
|
||||
// 修改接口
|
||||
}
|
||||
data.label = data.label2
|
||||
data.label2 = ''
|
||||
curId.value = NaN
|
||||
}
|
||||
|
||||
// 失焦
|
||||
const handleInputCancel = (node: Node, data: Data) => {
|
||||
if (data.id === Infinity) {
|
||||
remove(node, data)
|
||||
}
|
||||
curId.value = NaN
|
||||
}
|
||||
|
||||
const append = (data: Data) => {
|
||||
curId.value = Infinity
|
||||
const newChild = { id: curId.value, label2: 'test', children: [] }
|
||||
if (!data.children) {
|
||||
data.children = []
|
||||
}
|
||||
data.children.push(newChild)
|
||||
dataSource.value = [...dataSource.value]
|
||||
nextTick(() => {
|
||||
inputRef.value.focus()
|
||||
})
|
||||
}
|
||||
|
||||
const remove = (node: Node, data: Data) => {
|
||||
const parent = node.parent
|
||||
const children: Tree[] = parent?.data.children || parent?.data
|
||||
const index = children.findIndex((d) => d.id === data.id)
|
||||
children.splice(index, 1)
|
||||
dataSource.value = [...dataSource.value]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#category-tree {
|
||||
.category-tree-node {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 14px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
56
src/views/supplier/xsSwitch/mock.ts
Normal file
56
src/views/supplier/xsSwitch/mock.ts
Normal file
@ -0,0 +1,56 @@
|
||||
interface Tree {
|
||||
id: number
|
||||
label: string
|
||||
children?: Tree[]
|
||||
}
|
||||
|
||||
export const dataSource = ref<Tree[]>([
|
||||
{
|
||||
id: 1,
|
||||
label: 'Level one 1',
|
||||
children: [
|
||||
{
|
||||
id: 4,
|
||||
label: 'Level two 1-1',
|
||||
children: [
|
||||
{
|
||||
id: 9,
|
||||
label: 'Level three 1-1-1'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
label: 'Level three 1-1-2'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: 'Level one 2',
|
||||
children: [
|
||||
{
|
||||
id: 5,
|
||||
label: 'Level two 2-1'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
label: 'Level two 2-2'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
label: 'Level one 3',
|
||||
children: [
|
||||
{
|
||||
id: 7,
|
||||
label: 'Level two 3-1'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
label: 'Level two 3-2'
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
39
src/views/supplier/xsSwitch/use-drag.ts
Normal file
39
src/views/supplier/xsSwitch/use-drag.ts
Normal file
@ -0,0 +1,39 @@
|
||||
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) => {
|
||||
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)
|
||||
}
|
||||
@ -24,5 +24,5 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{ list: [] }>()
|
||||
const props = defineProps<{ list: any[] }>()
|
||||
</script>
|
||||
|
||||
44
src/views/supplier/xsWhitelist/editor.vue
Normal file
44
src/views/supplier/xsWhitelist/editor.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div style="border: 1px solid #ccc">
|
||||
<Toolbar
|
||||
style="border-bottom: 1px solid #ccc"
|
||||
:editor="editorRef"
|
||||
:defaultConfig="{}"
|
||||
mode="default"
|
||||
/>
|
||||
<Editor
|
||||
style="height: 500px; overflow-y: hidden"
|
||||
v-model="model"
|
||||
:defaultConfig="{ placeholder: '请输入内容...' }"
|
||||
mode="default"
|
||||
@onCreated="
|
||||
(editor: any) => {
|
||||
editorRef = editor
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import '@wangeditor/editor/dist/css/style.css'
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
||||
|
||||
const model = defineModel()
|
||||
|
||||
// 模拟 ajax 异步获取内容
|
||||
/* onMounted(() => {
|
||||
setTimeout(() => {
|
||||
// model.value = '<p>模拟 Ajax 异步设置内容</p>'
|
||||
}, 1500)
|
||||
}) */
|
||||
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef()
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value
|
||||
if (editor == null) return
|
||||
editor.destroy()
|
||||
})
|
||||
</script>
|
||||
@ -19,33 +19,35 @@
|
||||
</el-card>
|
||||
<el-card class="mt-2 sku-info">
|
||||
<h4 class="font-bold mb-2">SKU信息:</h4>
|
||||
<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>
|
||||
<dd class="flex mb-2">
|
||||
<div v-for="child in item.options" :key="item.typeId + '-' + child.id">
|
||||
<el-tag closable effect="plain" :type="colors[index % 4]" class="mr-1">{{
|
||||
child.name
|
||||
}}</el-tag>
|
||||
</div>
|
||||
<el-input
|
||||
v-if="inputVisibles[index]"
|
||||
ref="InputRefs"
|
||||
v-model="inputValue"
|
||||
class="w-20"
|
||||
size="small"
|
||||
@keyup.enter="handleInputConfirm(index)"
|
||||
@blur="handleInputConfirm(index)"
|
||||
/>
|
||||
<el-button
|
||||
v-else
|
||||
class="button-new-tag ml-1"
|
||||
size="small"
|
||||
@click="onAddCategoryDataOption(index)"
|
||||
>
|
||||
+ 新增
|
||||
</el-button>
|
||||
</dd>
|
||||
</dl>
|
||||
<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>
|
||||
<dd class="flex mb-2">
|
||||
<div v-for="child in item.options" :key="item.typeId + '-' + child.id">
|
||||
<el-tag closable effect="plain" :type="colors[index % 4]" class="mr-1">{{
|
||||
child.name
|
||||
}}</el-tag>
|
||||
</div>
|
||||
<el-input
|
||||
v-if="inputVisibles[index]"
|
||||
ref="InputRefs"
|
||||
v-model="inputValue"
|
||||
class="w-20"
|
||||
size="small"
|
||||
@keyup.enter="handleInputConfirm(index)"
|
||||
@blur="handleInputConfirm(index)"
|
||||
/>
|
||||
<el-button
|
||||
v-else
|
||||
class="button-new-tag ml-1"
|
||||
size="small"
|
||||
@click="onAddCategoryDataOption(index)"
|
||||
>
|
||||
+ 新增
|
||||
</el-button>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<h4 class="my-1">价格配置:</h4>
|
||||
<div
|
||||
@ -57,16 +59,19 @@
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card>
|
||||
<h4 class="font-bold mb-2">SKU信息:</h4>
|
||||
<h4 class="font-bold mb-2">内容编辑:</h4>
|
||||
<wan-editor v-model="htmlContent"></wan-editor>
|
||||
</el-card>
|
||||
<el-button type="primary" @click="onSubmit()">提交</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { initConfig } from './config'
|
||||
import { categoryDataMock, categoryOptionsMock } from './mock'
|
||||
import { generateTargetData, useCategoryAddOption, colors } from './use-method'
|
||||
import { generateTargetData, useCategoryAddOption, colors, useDrag } from './use-method'
|
||||
import categoryConfig from './category-config.vue'
|
||||
import wanEditor from './editor.vue'
|
||||
|
||||
const { $dialog, dialog1 } = toRefs(initConfig().value!)
|
||||
$dialog.value.config = dialog1.value
|
||||
@ -76,8 +81,16 @@ const categoryData = ref(categoryDataMock)
|
||||
const { inputValue, InputRefs, inputVisibles, onAddCategoryDataOption, handleInputConfirm } =
|
||||
useCategoryAddOption(categoryData)
|
||||
|
||||
const { dragRef, createDrag } = useDrag(categoryData)
|
||||
createDrag()
|
||||
const list = computed(() => generateTargetData(categoryData.value))
|
||||
console.warn('----- my data is list.value: ', list.value)
|
||||
|
||||
const htmlContent = ref('自定义')
|
||||
|
||||
const onSubmit = () => {
|
||||
console.warn('----- my data is htmlContent: ', htmlContent.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useDraggable } from 'vue-draggable-plus'
|
||||
// 定义源数据的结构类型
|
||||
interface Option {
|
||||
id: number
|
||||
@ -50,11 +51,10 @@ export const generateTargetData = (categoryData: CategoryDataMock): TargetData[]
|
||||
return result
|
||||
}
|
||||
|
||||
//
|
||||
// 添加类目
|
||||
export const useCategoryAddOption = (categoryData: any) => {
|
||||
|
||||
const inputVisibles = ref(Array.from({length: categoryData.value.length }, (_) => false))
|
||||
|
||||
const inputVisibles = ref(Array.from({ length: categoryData.value.length }, (_) => false))
|
||||
|
||||
const InputRefs = ref()
|
||||
const inputValue = ref('')
|
||||
const onAddCategoryDataOption = (index: number) => {
|
||||
@ -65,12 +65,33 @@ export const useCategoryAddOption = (categoryData: any) => {
|
||||
}
|
||||
const handleInputConfirm = (index: number) => {
|
||||
if (inputValue.value) {
|
||||
categoryData.value[index].options.push({ id: inputValue.value, name: inputValue.value, isAdd: true })
|
||||
categoryData.value[index].options.push({
|
||||
id: inputValue.value,
|
||||
name: inputValue.value,
|
||||
isAdd: true
|
||||
})
|
||||
}
|
||||
inputVisibles.value[index] = false
|
||||
inputValue.value = ''
|
||||
}
|
||||
return { inputVisibles, InputRefs, inputValue, onAddCategoryDataOption, handleInputConfirm}
|
||||
return { inputVisibles, InputRefs, inputValue, onAddCategoryDataOption, handleInputConfirm }
|
||||
}
|
||||
|
||||
export const colors = ['primary', 'success', 'warning', 'danger', 'info']
|
||||
|
||||
export const useDrag = (list: any) => {
|
||||
const dragRef = ref()
|
||||
const createDrag = () => {
|
||||
const { start } = useDraggable(dragRef, list, {
|
||||
animation: 150,
|
||||
ghostClass: 'ghost',
|
||||
onStart() {
|
||||
console.log('start')
|
||||
},
|
||||
onUpdate() {
|
||||
console.log('update')
|
||||
}
|
||||
})
|
||||
}
|
||||
return { dragRef, createDrag }
|
||||
}
|
||||
|
||||
@ -1170,7 +1170,7 @@
|
||||
|
||||
"@wangeditor/editor-for-vue@^5.1.12":
|
||||
version "5.1.12"
|
||||
resolved "https://registry.yarnpkg.com/@wangeditor/editor-for-vue/-/editor-for-vue-5.1.12.tgz#f7d5f239b39cdfc01d31151488de8443fe6edc64"
|
||||
resolved "https://registry.npmmirror.com/@wangeditor/editor-for-vue/-/editor-for-vue-5.1.12.tgz#f7d5f239b39cdfc01d31151488de8443fe6edc64"
|
||||
integrity sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==
|
||||
|
||||
"@wangeditor/editor@^5.1.23":
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user