feat: auto-import 优化+商品属性值排序

This commit is contained in:
zc 2025-11-03 23:02:22 +08:00
parent cf6aa9033e
commit 8fc07dd9cd
8 changed files with 204 additions and 120 deletions

View File

@ -1,7 +1,7 @@
const login = { const login = {
apiGetUserRoleInfo: ['/user/getRoleUserInfo'], // 获取用户信息及权限 apiGetUserRoleInfo: ['/user/getRoleUserInfo'], // 获取用户信息及权限
apiLogout: ['/login/out'], // 退出登录 apiLogout: ['/login/out'], // 退出登录
login: ['/login'] // 登录 login: ['/user/login'] // 登录
} }
export default login export default login

154
src/auto-import.d.ts vendored
View File

@ -6,95 +6,77 @@
// biome-ignore lint: disable // biome-ignore lint: disable
export {} export {}
declare global { declare global {
const EffectScope: (typeof import('vue'))['EffectScope'] const EffectScope: typeof import('vue')['EffectScope']
const api: (typeof import('src/api/index'))['api'] const api: typeof import('@/api/index')['api']
const computed: (typeof import('vue'))['computed'] const computed: typeof import('vue')['computed']
const createApp: (typeof import('vue'))['createApp'] const createApp: typeof import('vue')['createApp']
const customRef: (typeof import('vue'))['customRef'] const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: (typeof import('vue'))['defineAsyncComponent'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: (typeof import('vue'))['defineComponent'] const defineComponent: typeof import('vue')['defineComponent']
const effectScope: (typeof import('vue'))['effectScope'] const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: (typeof import('vue'))['getCurrentInstance'] const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: (typeof import('vue'))['getCurrentScope'] const getCurrentScope: typeof import('vue')['getCurrentScope']
const getCurrentWatcher: (typeof import('vue'))['getCurrentWatcher'] const getCurrentWatcher: typeof import('vue')['getCurrentWatcher']
const h: (typeof import('vue'))['h'] const h: typeof import('vue')['h']
const handleInit: (typeof import('src/utils/page/index'))['handleInit'] const handleInit: typeof import('@/utils/page/index')['handleInit']
const handleMessageBox: (typeof import('src/utils/page/index'))['handleMessageBox'] const handleMessageBox: typeof import('@/utils/page/index')['handleMessageBox']
const inject: (typeof import('vue'))['inject'] const inject: typeof import('vue')['inject']
const isProxy: (typeof import('vue'))['isProxy'] const isProxy: typeof import('vue')['isProxy']
const isReactive: (typeof import('vue'))['isReactive'] const isReactive: typeof import('vue')['isReactive']
const isReadonly: (typeof import('vue'))['isReadonly'] const isReadonly: typeof import('vue')['isReadonly']
const isRef: (typeof import('vue'))['isRef'] const isRef: typeof import('vue')['isRef']
const isShallow: (typeof import('vue'))['isShallow'] const isShallow: typeof import('vue')['isShallow']
const markRaw: (typeof import('vue'))['markRaw'] const markRaw: typeof import('vue')['markRaw']
const nextTick: (typeof import('vue'))['nextTick'] const nextTick: typeof import('vue')['nextTick']
const onActivated: (typeof import('vue'))['onActivated'] const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: (typeof import('vue'))['onBeforeMount'] const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeRouteLeave: (typeof import('vue-router'))['onBeforeRouteLeave'] const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
const onBeforeRouteUpdate: (typeof import('vue-router'))['onBeforeRouteUpdate'] const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
const onBeforeUnmount: (typeof import('vue'))['onBeforeUnmount'] const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: (typeof import('vue'))['onBeforeUpdate'] const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: (typeof import('vue'))['onDeactivated'] const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: (typeof import('vue'))['onErrorCaptured'] const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: (typeof import('vue'))['onMounted'] const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: (typeof import('vue'))['onRenderTracked'] const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: (typeof import('vue'))['onRenderTriggered'] const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: (typeof import('vue'))['onScopeDispose'] const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: (typeof import('vue'))['onServerPrefetch'] const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: (typeof import('vue'))['onUnmounted'] const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: (typeof import('vue'))['onUpdated'] const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: (typeof import('vue'))['onWatcherCleanup'] const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const pageConfig: (typeof import('src/utils/page/config'))['pageConfig'] const pageConfig: typeof import('@/utils/page/config')['pageConfig']
const provide: (typeof import('vue'))['provide'] const provide: typeof import('vue')['provide']
const reactive: (typeof import('vue'))['reactive'] const reactive: typeof import('vue')['reactive']
const readonly: (typeof import('vue'))['readonly'] const readonly: typeof import('vue')['readonly']
const ref: (typeof import('vue'))['ref'] const ref: typeof import('vue')['ref']
const resolveComponent: (typeof import('vue'))['resolveComponent'] const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: (typeof import('vue'))['shallowReactive'] const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: (typeof import('vue'))['shallowReadonly'] const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: (typeof import('vue'))['shallowRef'] const shallowRef: typeof import('vue')['shallowRef']
const toRaw: (typeof import('vue'))['toRaw'] const toRaw: typeof import('vue')['toRaw']
const toRef: (typeof import('vue'))['toRef'] const toRef: typeof import('vue')['toRef']
const toRefs: (typeof import('vue'))['toRefs'] const toRefs: typeof import('vue')['toRefs']
const toValue: (typeof import('vue'))['toValue'] const toValue: typeof import('vue')['toValue']
const triggerRef: (typeof import('vue'))['triggerRef'] const triggerRef: typeof import('vue')['triggerRef']
const unref: (typeof import('vue'))['unref'] const unref: typeof import('vue')['unref']
const useAttrs: (typeof import('vue'))['useAttrs'] const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: (typeof import('vue'))['useCssModule'] const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: (typeof import('vue'))['useCssVars'] const useCssVars: typeof import('vue')['useCssVars']
const useId: (typeof import('vue'))['useId'] const useId: typeof import('vue')['useId']
const useLink: (typeof import('vue-router'))['useLink'] const useLink: typeof import('vue-router')['useLink']
const useModel: (typeof import('vue'))['useModel'] const useModel: typeof import('vue')['useModel']
const useRoute: (typeof import('vue-router'))['useRoute'] const useRoute: typeof import('vue-router')['useRoute']
const useRouter: (typeof import('vue-router'))['useRouter'] const useRouter: typeof import('vue-router')['useRouter']
const useSlots: (typeof import('vue'))['useSlots'] const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: (typeof import('vue'))['useTemplateRef'] const useTemplateRef: typeof import('vue')['useTemplateRef']
const watch: (typeof import('vue'))['watch'] const watch: typeof import('vue')['watch']
const watchEffect: (typeof import('vue'))['watchEffect'] const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: (typeof import('vue'))['watchPostEffect'] const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: (typeof import('vue'))['watchSyncEffect'] const watchSyncEffect: typeof import('vue')['watchSyncEffect']
} }
// for type re-export // for type re-export
declare global { declare global {
// @ts-ignore // @ts-ignore
export type { export type { Component, Slot, Slots, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, ShallowRef, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
Component,
Slot,
Slots,
ComponentPublicInstance,
ComputedRef,
DirectiveBinding,
ExtractDefaultPropTypes,
ExtractPropTypes,
ExtractPublicPropTypes,
InjectionKey,
PropType,
Ref,
ShallowRef,
MaybeRef,
MaybeRefOrGetter,
VNode,
WritableComputedRef
} from 'vue'
import('vue') import('vue')
} }

View File

@ -126,7 +126,7 @@
v-model="editPropertyTypeValue" v-model="editPropertyTypeValue"
size="small" size="small"
class="w-20" class="w-20"
@keyup.enter="handleEditPropertyTypeConfirm(item, skuList)" @keyup.enter="handleEditPropertyTypeConfirm(item, skuList, adminCategoryData)"
@blur="editPropertyTypeIndex = NaN" @blur="editPropertyTypeIndex = NaN"
/> />
</dt> </dt>
@ -138,7 +138,7 @@
class="w-20" class="w-20"
size="small" size="small"
v-model="inputEditPropertyValue" v-model="inputEditPropertyValue"
@keyup.enter="onEditPropertyValue(child, skuList)" @keyup.enter="onEditPropertyValue(child, skuList, item.vvPropertyValueList)"
@blur="curPropertyValueId = ''" @blur="curPropertyValueId = ''"
></el-input> ></el-input>
<el-tag <el-tag

View File

@ -1,3 +1,5 @@
import { ElMessage } from 'element-plus'
export const editPropertyTypeIndex = ref(NaN) export const editPropertyTypeIndex = ref(NaN)
export const editPropertyTypeValue = ref('') export const editPropertyTypeValue = ref('')
export const editPropertyTypeInputRef = ref() export const editPropertyTypeInputRef = ref()
@ -8,8 +10,20 @@ export const onEditPropertyTypeBtn = (index: number) => {
}) })
} }
export const handleEditPropertyTypeConfirm = async (item: any, skuList: any) => { export const handleEditPropertyTypeConfirm = async (
item: any,
skuList: any,
adminCategoryData: any
) => {
const oldCategoryPropertyName = item.categoryPropertyName const oldCategoryPropertyName = item.categoryPropertyName
if (
oldCategoryPropertyName !== editPropertyTypeValue.value &&
adminCategoryData.find((item: any) => item.categoryPropertyName === editPropertyTypeValue.value)
) {
editPropertyTypeValue.value = ''
ElMessage.error('属性类型已存在,请重新输入')
return
}
item.categoryPropertyName = editPropertyTypeValue.value item.categoryPropertyName = editPropertyTypeValue.value
item.vvPropertyValueList.forEach((item: any) => { item.vvPropertyValueList.forEach((item: any) => {
item.categoryPropertyName = editPropertyTypeValue.value item.categoryPropertyName = editPropertyTypeValue.value
@ -56,8 +70,17 @@ export const onClickPropertyValue = (item: any, child: any) => {
}) })
} }
export const onEditPropertyValue = (child: any, skuList: any) => { export const onEditPropertyValue = (child: any, skuList: any, vvPropertyValueList: any) => {
const oldCategoryPropertyValue = child.categoryPropertyValue const oldCategoryPropertyValue = child.categoryPropertyValue
if (
oldCategoryPropertyValue !== inputEditPropertyValue.value &&
vvPropertyValueList.find(
(item: any) => item.categoryPropertyValue === inputEditPropertyValue.value
)
) {
ElMessage.error('属性值已存在,请重新输入')
return
}
child.categoryPropertyValue = inputEditPropertyValue.value child.categoryPropertyValue = inputEditPropertyValue.value
handleEditSkuListPropertyValue( handleEditSkuListPropertyValue(
child.categoryPropertyName, child.categoryPropertyName,

View File

@ -1,3 +1,4 @@
import { ElMessage } from 'element-plus'
import { useDraggable } from 'vue-draggable-plus' import { useDraggable } from 'vue-draggable-plus'
// 定义源数据的结构类型 // 定义源数据的结构类型
interface Option { interface Option {
@ -28,7 +29,7 @@ export interface TSkuList {
vvSkuPropertyValueList: any[] vvSkuPropertyValueList: any[]
} }
// 当改变后台类目的时候重新创建sku列表 /* // sku列表
export const generateSkuList = (adminCategoryData: CategoryDataMock): TSkuList[] => { export const generateSkuList = (adminCategoryData: CategoryDataMock): TSkuList[] => {
if (adminCategoryData.length === 0) { if (adminCategoryData.length === 0) {
return [] return []
@ -62,7 +63,7 @@ export const generateSkuList = (adminCategoryData: CategoryDataMock): TSkuList[]
combine(0, []) combine(0, [])
return result return result
} } */
// 增加属性、删除属性 // 增加属性、删除属性
export const usePropertyValue = () => { export const usePropertyValue = () => {
@ -105,6 +106,15 @@ export const usePropertyValue = () => {
} }
// 新增回车确认 // 新增回车确认
const handleInputConfirm = (lineIndex: number, skuList: TSkuList[] = []) => { const handleInputConfirm = (lineIndex: number, skuList: TSkuList[] = []) => {
if (
adminCategoryData[lineIndex].vvPropertyValueList.find(
(item: any) => item.categoryPropertyValue === inputValue.value
)
) {
ElMessage.error('属性值已存在,请重新输入')
inputValue.value = ''
return
}
if (inputValue.value) { if (inputValue.value) {
const id = Math.random() const id = Math.random()
const newData = { const newData = {
@ -117,6 +127,7 @@ export const usePropertyValue = () => {
if (skuList?.length && skuList[0].vvSkuPropertyValueList.length < adminCategoryData.length) { if (skuList?.length && skuList[0].vvSkuPropertyValueList.length < adminCategoryData.length) {
skuList.forEach((item: TSkuList) => { skuList.forEach((item: TSkuList) => {
item.name += '-' + inputValue.value item.name += '-' + inputValue.value
item.categoryPropertyNames += '-' + adminCategoryData[lineIndex].categoryPropertyName
item.vvSkuPropertyValueList.push({ ...newData }) item.vvSkuPropertyValueList.push({ ...newData })
}) })
} else { } else {
@ -126,7 +137,7 @@ export const usePropertyValue = () => {
inputVisibles.value[lineIndex] = false inputVisibles.value[lineIndex] = false
inputValue.value = '' inputValue.value = ''
} }
/*
// 新增属性的时候创建数据 // 新增属性的时候创建数据
const generateAddSkuList = ( const generateAddSkuList = (
lineIndex: number, lineIndex: number,
@ -169,8 +180,13 @@ export const usePropertyValue = () => {
//bug //bug
console.warn('----- my data is result111: ', result) console.warn('----- my data is result111: ', result)
return result return result
} } */
const onAddPropertyLine = (categoryId: number, val: string) => { const onAddPropertyLine = (categoryId: number, val: string) => {
if (adminCategoryData.find((item: any) => item.categoryPropertyName === val)) {
ElMessage.error('属性类型已存在,请重新输入')
inputPropertyLineValue.value = ''
return
}
visiblePropertyLine.value = false visiblePropertyLine.value = false
console.warn('----- my data is inputPropertyLineValue: ', val) console.warn('----- my data is inputPropertyLineValue: ', val)
adminCategoryData.push({ adminCategoryData.push({
@ -275,3 +291,64 @@ export const handleCheckChange = (
data[type + 'CategoryIds'] = [...checkedStatus.halfCheckedKeys, ...checkedStatus.checkedKeys] data[type + 'CategoryIds'] = [...checkedStatus.halfCheckedKeys, ...checkedStatus.checkedKeys]
return checkedData return checkedData
} }
// 当改变后台类目的时候重新创建sku列表
export const generateSkuList = (categories: CategoryDataMock): TSkuList[] => {
if (categories.length === 0) {
return []
}
// 1) 提取每个分类里可用的值(按传入顺序保序)
const arrays = categories.map((cat) => cat.vvPropertyValueList || [])
// 若任一分类无可用值,则无组合
if (arrays.some((arr) => arr.length === 0)) return []
// 2) 做笛卡尔积,保持输入顺序
let combos = arrays[0].map((x) => [x])
for (let i = 1; i < arrays.length; i++) {
const next = []
for (const combo of combos) {
for (const x of arrays[i]) {
next.push([...combo, x])
}
}
combos = next
}
// 3) 生成目标对象name 由 value 用 '-' 拼接originArray 记录来源对象
return combos.map((combo) => {
const serialNo = combo.map((opt) => opt.id.toString()).join('-')
const name = combo.map((opt) => String(opt.categoryPropertyValue)).join('-')
const categoryPropertyNames = combo.map((opt) => String(opt.categoryPropertyValue)).join('-')
const originArray = combo
return {
serialNo,
name,
categoryPropertyNames,
salePrice: '',
stock: 0,
originPrice: 0,
imageUrl: '',
promotionPrice: '',
vvSkuPropertyValueList: originArray
}
})
}
// 新增属性值的时候创建数据
const generateAddSkuList = (
lineIndex: number,
addData: Option,
adminCategoryData: CategoryDataMock
): TSkuList[] => {
const filterCategoryData: CategoryDataMock = adminCategoryData.map((category, i) => {
if (i === lineIndex) {
return {
...category,
vvPropertyValueList: category.vvPropertyValueList.filter((item) => item.id === addData.id)
}
}
return category
})
return generateSkuList(filterCategoryData)
}

View File

@ -1,17 +1,18 @@
type TCommodityType = 'online' | 'down' | 'draft' | 'delete' type TCommodityType = 'all' | 'online' | 'down' | 'draft' | 'delete'
export const useCommodityType = (search: any, table: any) => { export const useCommodityType = (search: any, table: any) => {
let beforeValue = 'online' let beforeValue = 'online'
const commodityType = ref<TCommodityType>('online') const commodityType = ref<TCommodityType>('online')
const commodityTypeOptions = [ const commodityTypeOptions = [
{ label: '所有商品', value: 'all' },
{ label: '在线商品', value: 'online' }, { label: '在线商品', value: 'online' },
{ label: '下架商品', value: 'down' }, { label: '下架商品', value: 'down' },
{ label: '草稿', value: 'draft' }, { label: '草稿', value: 'draft' },
{ label: '已删商品', value: 'delete' } { label: '已删商品', value: 'delete' }
] ]
const cacheTableData = { online: [], down: [], draft: [], delete: [] } // 缓存表单数据 const cacheTableData = { all: [], online: [], down: [], draft: [], delete: [] } // 缓存表单数据
const cacheTablePage = { online: {}, down: {}, draft: {}, delete: {} } // 缓存表单页面数据 const cacheTablePage = { all: {}, online: {}, down: {}, draft: {}, delete: {} } // 缓存表单页面数据
const onChangeCommodityType = (type: 'online' | 'down' | 'draft' | 'delete') => { const onChangeCommodityType = (type: 'all' | 'online' | 'down' | 'draft' | 'delete') => {
cacheTableData[beforeValue as typeof type] = toRaw(table.value.$data) cacheTableData[beforeValue as typeof type] = toRaw(table.value.$data)
cacheTablePage[beforeValue as typeof type] = toRaw(table.value.$pages) cacheTablePage[beforeValue as typeof type] = toRaw(table.value.$pages)
beforeValue = type beforeValue = type
@ -22,13 +23,21 @@ export const useCommodityType = (search: any, table: any) => {
table.value.$data = cacheTableData[type] table.value.$data = cacheTableData[type]
table.value.$pages = cacheTablePage[type] table.value.$pages = cacheTablePage[type]
} else { } else {
table.value.$onGetData(table.value, 1, search) if (type === 'all') {
search.value.$data = { status: '' }
table.value.$onGetData(table.value, 1, search)
} else {
table.value.$onGetData(table.value, 1, search)
}
} }
} }
const handleSetTableConfig = (table: any, type: 'online' | 'down' | 'draft' | 'delete') => { const handleSetTableConfig = (
const btnOnlineName = { online: '下架', down: '上架', draft: '', delete: '' } // 上下架按钮名称 table: any,
const btnHomeName = { online: '加入首页', down: '', draft: '', delete: '' } // 上下架按钮名称 type: 'all' | 'online' | 'down' | 'draft' | 'delete'
const btnWidth = { online: '230', down: '180', draft: '160', delete: '160' } // 上下架按钮名称 ) => {
const btnOnlineName = { all: '', online: '下架', down: '上架', draft: '', delete: '' } // 上下架按钮名称
const btnHomeName = { all: '', online: '加入首页', down: '', draft: '', delete: '' } // 上下架按钮名称
const btnWidth = { all: '180', online: '230', down: '180', draft: '160', delete: '160' } // 上下架按钮名称
table.value.btn.$attr.names[2] = btnHomeName[type] table.value.btn.$attr.names[2] = btnHomeName[type]
table.value.btn.$attr.names[3] = btnOnlineName[type] table.value.btn.$attr.names[3] = btnOnlineName[type]
table.value.btn.$attr.width = btnWidth[type] table.value.btn.$attr.width = btnWidth[type]

View File

@ -19,17 +19,10 @@ const loginRules = reactive({
const handleLogin = () => { const handleLogin = () => {
loginFormRef.value.validate((valid) => { loginFormRef.value.validate((valid) => {
if (valid) { if (valid) {
api.login.login api.login.login.post(loginData).then((res) => {
.post(loginData) router.push({ path: '/home' })
.then((res) => { user.onLogin({ tokenValue: res.data.token, tokenName: 'mmToken' })
if (res.code === 200) { })
router.push({ path: '/home' })
}
})
.finally(() => {
user.onLogin({ tokenValue: '123', tokenName: '123' })
router.push({ path: '/home' })
})
} else { } else {
return false return false
} }
@ -48,10 +41,10 @@ const handleLogin = () => {
@keyup.enter="handleLogin" @keyup.enter="handleLogin"
class="login_form" class="login_form"
> >
<el-form-item label="手机号码" prop="username"> <el-form-item label="用户名" prop="username">
<el-input <el-input
v-model="loginData.username" v-model="loginData.username"
placeholder="请输入手机号码" placeholder="请输入用户名"
type="text" type="text"
maxlength="11" maxlength="11"
class="login_form__input" class="login_form__input"

View File

@ -36,9 +36,9 @@ export default defineConfig({
imports: [ imports: [
'vue', 'vue',
'vue-router', 'vue-router',
{ from: '/src/utils/page/config', imports: ['pageConfig'] }, { from: '@/utils/page/config', imports: ['pageConfig'] },
{ from: '/src/utils/page/index', imports: ['handleInit', 'handleMessageBox'] }, { from: '@/utils/page/index', imports: ['handleInit', 'handleMessageBox'] },
{ from: '/src/api/index.ts', imports: ['api'] } { from: '@/api/index', imports: ['api'] }
], ],
dts: 'src/auto-import.d.ts' dts: 'src/auto-import.d.ts'
}), }),