salary
qsh 6 months ago
parent de522af86f
commit a557255b4a
  1. 2
      .env.base
  2. 2
      .env.dev
  3. 2
      .env.front
  4. 2
      .env.pro
  5. 2
      .env.stage
  6. 2
      .env.static
  7. 2
      .env.test
  8. 45
      src/api/infra/file/index.ts
  9. 10
      src/api/mall/product/brand.ts
  10. 10
      src/api/mall/product/category.ts
  11. 10
      src/api/mall/product/index.js
  12. 62
      src/components/Editor/src/Editor.vue
  13. 10
      src/components/Search/src/Search.vue
  14. 98
      src/components/UploadFile/src/UploadImgs.vue
  15. 93
      src/components/UploadFile/src/useUpload.ts
  16. 2
      src/views/Basic/Dept/DeptForm.vue
  17. 53
      src/views/Clue/Order/index.vue
  18. 65
      src/views/Clue/Pool/index.vue
  19. 50
      src/views/Clue/Set/Comp/GeneralSet.vue
  20. 6
      src/views/Clue/Set/index.vue
  21. 7
      src/views/MiniMall/Inventory/index.vue
  22. 64
      src/views/MiniMall/MallSet/Comp/BrandSet.vue
  23. 63
      src/views/MiniMall/MallSet/Comp/CategorySet.vue
  24. 42
      src/views/MiniMall/MallSet/Comp/DialogBrand.vue
  25. 42
      src/views/MiniMall/MallSet/Comp/DialogCategory.vue
  26. 25
      src/views/MiniMall/MallSet/index.vue
  27. 70
      src/views/MiniMall/Product/add.vue
  28. 26
      src/views/MiniMall/Product/index.vue
  29. 58
      src/views/MiniMall/Purchase/index.vue

@ -7,7 +7,7 @@ VITE_DEV=true
VITE_BASE_URL='http://118.31.23.45:48080' VITE_BASE_URL='http://118.31.23.45:48080'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH=/dev-api VITE_API_BASEPATH=/dev-api

@ -7,7 +7,7 @@ VITE_DEV=false
VITE_BASE_URL='http://localhost:48080' VITE_BASE_URL='http://localhost:48080'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH=/dev-api VITE_API_BASEPATH=/dev-api

@ -7,7 +7,7 @@ VITE_DEV=true
VITE_BASE_URL='http://118.31.23.45:48080' VITE_BASE_URL='http://118.31.23.45:48080'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH=/dev-api VITE_API_BASEPATH=/dev-api

@ -7,7 +7,7 @@ VITE_DEV=false
VITE_BASE_URL='http://localhost:48080' VITE_BASE_URL='http://localhost:48080'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH= VITE_API_BASEPATH=

@ -7,7 +7,7 @@ VITE_DEV=false
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn' VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH= VITE_API_BASEPATH=

@ -7,7 +7,7 @@ VITE_DEV=false
VITE_BASE_URL='http://localhost:48080' VITE_BASE_URL='http://localhost:48080'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH= VITE_API_BASEPATH=

@ -7,7 +7,7 @@ VITE_DEV=false
VITE_BASE_URL='http://localhost:48080' VITE_BASE_URL='http://localhost:48080'
# 上传路径 # 上传路径
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload' VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
# 接口前缀 # 接口前缀
VITE_API_BASEPATH= VITE_API_BASEPATH=

@ -0,0 +1,45 @@
import request from '@/config/axios'
export interface FilePageReqVO extends PageParam {
path?: string
type?: string
createTime?: Date[]
}
// 文件预签名地址 Response VO
export interface FilePresignedUrlRespVO {
// 文件配置编号
configId: number
// 文件上传 URL
uploadUrl: string
// 文件 URL
url: string
}
// 查询文件列表
export const getFilePage = (params: FilePageReqVO) => {
return request.get({ url: '/infra/file/page', params })
}
// 删除文件
export const deleteFile = (id: number) => {
return request.delete({ url: '/infra/file/delete?id=' + id })
}
// 获取文件预签名地址
export const getFilePresignedUrl = (path: string) => {
return request.get<FilePresignedUrlRespVO>({
url: '/infra/file/presigned-url',
params: { path }
})
}
// 创建文件
export const createFile = (data: any) => {
return request.post({ url: '/infra/file/create', data })
}
// 上传文件
export const updateFile = (data: any) => {
return request.upload({ url: '/admin-api/system/file/upload', data })
}

@ -32,27 +32,27 @@ export interface BrandVO {
// 创建商品品牌 // 创建商品品牌
export const createBrand = (data: BrandVO) => { export const createBrand = (data: BrandVO) => {
return request.post({ url: '/product/brand/create', data }) return request.post({ url: '/admin-api/crm/erp-product-brand/create', data })
} }
// 更新商品品牌 // 更新商品品牌
export const updateBrand = (data: BrandVO) => { export const updateBrand = (data: BrandVO) => {
return request.put({ url: '/product/brand/update', data }) return request.put({ url: '/admin-api/crm/erp-product-brand/update', data })
} }
// 删除商品品牌 // 删除商品品牌
export const deleteBrand = (id: number) => { export const deleteBrand = (id: number) => {
return request.delete({ url: `/product/brand/delete?id=${id}` }) return request.delete({ url: `/admin-api/crm/erp-product-brand/delete?id=${id}` })
} }
// 获得商品品牌 // 获得商品品牌
export const getBrand = (id: number) => { export const getBrand = (id: number) => {
return request.get({ url: `/product/brand/get?id=${id}` }) return request.get({ url: `/admin-api/crm/erp-product-brand/get?id=${id}` })
} }
// 获得商品品牌列表 // 获得商品品牌列表
export const getBrandParam = (params: PageParam) => { export const getBrandParam = (params: PageParam) => {
return request.get({ url: '/product/brand/page', params }) return request.get({ url: '/admin-api/crm/erp-product-brand/page', params })
} }
// 获得商品品牌精简信息列表 // 获得商品品牌精简信息列表

@ -36,25 +36,25 @@ export interface CategoryVO {
// 创建商品分类 // 创建商品分类
export const createCategory = (data: CategoryVO) => { export const createCategory = (data: CategoryVO) => {
return request.post({ url: '/product/category/create', data }) return request.post({ url: '/admin-api/crm/erp-product-category/create', data })
} }
// 更新商品分类 // 更新商品分类
export const updateCategory = (data: CategoryVO) => { export const updateCategory = (data: CategoryVO) => {
return request.put({ url: '/product/category/update', data }) return request.put({ url: '/admin-api/crm/erp-product-category/update', data })
} }
// 删除商品分类 // 删除商品分类
export const deleteCategory = (id: number) => { export const deleteCategory = (id: number) => {
return request.delete({ url: `/product/category/delete?id=${id}` }) return request.delete({ url: `/admin-api/crm/erp-product-category/delete?id=${id}` })
} }
// 获得商品分类 // 获得商品分类
export const getCategory = (id: number) => { export const getCategory = (id: number) => {
return request.get({ url: `/product/category/get?id=${id}` }) return request.get({ url: `/admin-api/crm/erp-product-category/get?id=${id}` })
} }
// 获得商品分类列表 // 获得商品分类列表
export const getCategoryList = (params: any) => { export const getCategoryList = (params: any) => {
return request.get({ url: '/product/category/list', params }) return request.get({ url: '/admin-api/crm/erp-product-category/page', params })
} }

@ -1,25 +1,25 @@
import request from '@/config/axios' import request from '@/config/axios'
// 查询列表 // 查询列表
export const getProductPage = async (params) => { export const getProductPage = async (params) => {
return await request.get({ url: '/admin-api/crm/erp-product//page', params }) return await request.get({ url: '/admin-api/crm/erp-product/page', params })
} }
// 查询详情 // 查询详情
export const getProduct = async (id) => { export const getProduct = async (id) => {
return await request.get({ url: '/admin-api/crm/erp-product//get?id=' + id }) return await request.get({ url: '/admin-api/crm/erp-product/get?id=' + id })
} }
// 新增 // 新增
export const createProduct = async (data) => { export const createProduct = async (data) => {
return await request.post({ url: '/admin-api/crm/erp-product//create', data: data }) return await request.post({ url: '/admin-api/crm/erp-product/create', data: data })
} }
// 修改 // 修改
export const updateProduct = async (params) => { export const updateProduct = async (params) => {
return await request.put({ url: '/admin-api/crm/erp-product//update', data: params }) return await request.put({ url: '/admin-api/crm/erp-product/update', data: params })
} }
// 删除 // 删除
export const deleteProduct = async (id) => { export const deleteProduct = async (id) => {
return await request.delete({ url: '/admin-api/crm/erp-product//delete?id=' + id }) return await request.delete({ url: '/admin-api/crm/erp-product/delete?id=' + id })
} }

@ -6,7 +6,7 @@ import { propTypes } from '@/utils/propTypes'
import { isNumber } from '@/utils/is' import { isNumber } from '@/utils/is'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useLocaleStore } from '@/store/modules/locale' import { useLocaleStore } from '@/store/modules/locale'
import { getAccessToken, getTenantId } from '@/utils/auth' import { getAccessToken, getTenantId, getAppId } from '@/utils/auth'
type InsertFnType = (url: string, alt: string, href: string) => void type InsertFnType = (url: string, alt: string, href: string) => void
@ -103,7 +103,8 @@ const editorConfig = computed((): IEditorConfig => {
headers: { headers: {
Accept: '*', Accept: '*',
Authorization: 'Bearer ' + getAccessToken(), Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId() 'tenant-id': getTenantId(),
'instance-id': getAppId()
}, },
// cookie false // cookie false
@ -140,6 +141,63 @@ const editorConfig = computed((): IEditorConfig => {
customInsert(res: any, insertFn: InsertFnType) { customInsert(res: any, insertFn: InsertFnType) {
insertFn(res.data, 'image', res.data) insertFn(res.data, 'image', res.data)
} }
},
['uploadVideo']: {
server: import.meta.env.VITE_UPLOAD_URL,
// 2M
maxFileSize: 100 * 1024 * 1024,
// 100
maxNumberOfFiles: 10,
// ['image/*'] []
allowedFileTypes: ['video/*'],
// token formData
meta: { updateSupport: 0 },
// meta url false
metaWithUrl: true,
// http header
headers: {
Accept: '*',
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId(),
'instance-id': getAppId()
},
// cookie false
withCredentials: true,
// 10
timeout: 10 * 1000, // 5
// form-data fieldNamewangeditor-uploaded-image
fieldName: 'file',
//
onBeforeUpload(file: File) {
console.log(file)
return file
},
//
onProgress(progress: number) {
// progress 0-100
console.log('progress', progress)
},
onSuccess(file: File, res: any) {
console.log('onSuccess', file, res)
},
onFailed(file: File, res: any) {
alert(res.message)
console.log('onFailed', file, res)
},
onError(file: File, err: any, res: any) {
alert(err.message)
console.error('onError', file, err, res)
},
//
customInsert(res: any, insertFn: InsertFnType) {
insertFn(res.data, 'video', res.data)
}
} }
}, },
uploadImgShowBase64: true uploadImgShowBase64: true

@ -33,8 +33,8 @@ const props = defineProps({
.validate((v: string) => ['left', 'center', 'right'].includes(v)) .validate((v: string) => ['left', 'center', 'right'].includes(v))
.def('center'), .def('center'),
showLabel: propTypes.bool.def(false), showLabel: propTypes.bool.def(false),
showSearch: propTypes.bool.def(true), showSearch: propTypes.bool.def(false),
showReset: propTypes.bool.def(true), showReset: propTypes.bool.def(false),
// //
expand: propTypes.bool.def(false), expand: propTypes.bool.def(false),
// //
@ -199,10 +199,10 @@ initSearch()
</ElButton> </ElButton>
<!-- add by 芋艿补充在搜索后的按钮 --> <!-- add by 芋艿补充在搜索后的按钮 -->
<slot name="actionMore"></slot> <slot name="actionMore"></slot>
<ElButton v-if="expand" text @click="setVisible"> <!-- <ElButton v-if="expand" text @click="setVisible">
{{ t(visible ? 'common.shrink' : 'common.expand') }} {{ t(visible ? 'common.shrink' : 'common.expand') }}
<!-- <Icon :icon="visible ? 'ep:arrow-up' : 'ep:arrow-down'" /> --> <Icon :icon="visible ? 'ep:arrow-up' : 'ep:arrow-down'" />
</ElButton> </ElButton> -->
</div> </div>
</template> </template>
<template v-for="name in Object.keys($slots)" :key="name" #[name]> <template v-for="name in Object.keys($slots)" :key="name" #[name]>

@ -3,11 +3,12 @@
<el-upload <el-upload
v-model:file-list="fileList" v-model:file-list="fileList"
:accept="fileType.join(',')" :accept="fileType.join(',')"
:action="updateUrl" :action="uploadUrl"
:before-upload="beforeUpload" :before-upload="beforeUpload"
:class="['upload', drag ? 'no-border' : '']" :class="['upload', drag ? 'no-border' : '']"
:disabled="disabled"
:drag="drag" :drag="drag"
:headers="uploadHeaders" :http-request="httpRequest"
:limit="limit" :limit="limit"
:multiple="true" :multiple="true"
:on-error="uploadError" :on-error="uploadError"
@ -28,7 +29,7 @@
<Icon icon="ep:zoom-in" /> <Icon icon="ep:zoom-in" />
<span>查看</span> <span>查看</span>
</div> </div>
<div class="handle-icon" @click="handleRemove(file)"> <div v-if="!disabled" class="handle-icon" @click="handleRemove(file)">
<Icon icon="ep:delete" /> <Icon icon="ep:delete" />
<span>删除</span> <span>删除</span>
</div> </div>
@ -45,13 +46,14 @@
/> />
</div> </div>
</template> </template>
<script lang="ts" name="UploadImgs" setup> <script lang="ts" setup>
import { PropType } from 'vue'
import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus' import type { UploadFile, UploadProps, UploadUserFile } from 'element-plus'
import { ElNotification } from 'element-plus' import { ElNotification } from 'element-plus'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
import { getAccessToken, getTenantId } from '@/utils/auth' import { useUpload } from './useUpload'
defineOptions({ name: 'UploadImgs' })
const message = useMessage() // const message = useMessage() //
@ -68,11 +70,7 @@ type FileTypes =
| 'image/x-icon' | 'image/x-icon'
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
type: Array as PropType<UploadUserFile[]>,
required: true
},
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
drag: propTypes.bool.def(true), // ==> true drag: propTypes.bool.def(true), // ==> true
disabled: propTypes.bool.def(false), // ==> false disabled: propTypes.bool.def(false), // ==> false
limit: propTypes.number.def(5), // ==> 5 limit: propTypes.number.def(5), // ==> 5
@ -80,27 +78,14 @@ const props = defineProps({
fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // ==> ["image/jpeg", "image/png", "image/gif"] fileType: propTypes.array.def(['image/jpeg', 'image/png', 'image/gif']), // ==> ["image/jpeg", "image/png", "image/gif"]
height: propTypes.string.def('150px'), // ==> 150px height: propTypes.string.def('150px'), // ==> 150px
width: propTypes.string.def('150px'), // ==> 150px width: propTypes.string.def('150px'), // ==> 150px
borderRadius: propTypes.string.def('8px') // ==> 8px borderradius: propTypes.string.def('8px') // ==> 8px
}) })
const uploadHeaders = ref({ const { uploadUrl, httpRequest } = useUpload()
Authorization: 'Bearer ' + getAccessToken(),
'tenant-id': getTenantId()
})
const fileList = ref<UploadUserFile[]>() const fileList = ref<UploadUserFile[]>([])
// fix: const uploadNumber = ref<number>(0)
watch( const uploadList = ref<UploadUserFile[]>([])
() => props.modelValue,
(data) => {
if (!data) return
fileList.value = data
},
{
deep: true,
immediate: true
}
)
/** /**
* @description 文件上传之前判断 * @description 文件上传之前判断
* @param rawFile 上传的文件 * @param rawFile 上传的文件
@ -120,29 +105,60 @@ const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
message: `上传图片大小不能超过 ${props.fileSize}M!`, message: `上传图片大小不能超过 ${props.fileSize}M!`,
type: 'warning' type: 'warning'
}) })
uploadNumber.value++
return imgType.includes(rawFile.type as FileTypes) && imgSize return imgType.includes(rawFile.type as FileTypes) && imgSize
} }
// //
interface UploadEmits { interface UploadEmits {
(e: 'update:modelValue', value: UploadUserFile[]): void (e: 'update:modelValue', value: string[]): void
} }
const emit = defineEmits<UploadEmits>() const emit = defineEmits<UploadEmits>()
const uploadSuccess = (response, uploadFile: UploadFile) => { const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
if (!response) return
// TODO urlfileList
uploadFile.url = response.data
emit('update:modelValue', fileList.value)
message.success('上传成功') message.success('上传成功')
//
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
fileList.value.splice(index, 1)
uploadList.value.push({ name: res.data, url: res.data })
if (uploadList.value.length == uploadNumber.value) {
fileList.value.push(...uploadList.value)
uploadList.value = []
uploadNumber.value = 0
emitUpdateModelValue()
}
} }
//
watch(
() => props.modelValue,
(val: string | string[]) => {
if (!val) {
fileList.value = [] // fix
return
}
fileList.value = [] //
fileList.value.push(
...(val as string[]).map((url) => ({ name: url.substring(url.lastIndexOf('/') + 1), url }))
)
},
{ immediate: true, deep: true }
)
//
const emitUpdateModelValue = () => {
let result: string[] = fileList.value.map((file) => file.url!)
emit('update:modelValue', result)
}
// //
const handleRemove = (uploadFile: UploadFile) => { const handleRemove = (uploadFile: UploadFile) => {
fileList.value = fileList.value.filter( fileList.value = fileList.value.filter(
(item) => item.url !== uploadFile.url || item.name !== uploadFile.name (item) => item.url !== uploadFile.url || item.name !== uploadFile.name
) )
emit('update:modelValue', fileList.value) emit(
'update:modelValue',
fileList.value.map((file) => file.url!)
)
} }
// //
@ -216,7 +232,7 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
padding: 0; padding: 0;
overflow: hidden; overflow: hidden;
border: 1px dashed var(--el-border-color-darker); border: 1px dashed var(--el-border-color-darker);
border-radius: v-bind(borderRadius); border-radius: v-bind(borderradius);
&:hover { &:hover {
border: 1px dashed var(--el-color-primary); border: 1px dashed var(--el-color-primary);
@ -233,7 +249,7 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
width: v-bind(width); width: v-bind(width);
height: v-bind(height); height: v-bind(height);
background-color: transparent; background-color: transparent;
border-radius: v-bind(borderRadius); border-radius: v-bind(borderradius);
} }
.upload-image { .upload-image {
@ -246,16 +262,16 @@ const handlePictureCardPreview: UploadProps['onPreview'] = (uploadFile) => {
position: absolute; position: absolute;
top: 0; top: 0;
right: 0; right: 0;
box-sizing: border-box;
display: flex; display: flex;
align-items: center;
justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
cursor: pointer; cursor: pointer;
background: rgb(0 0 0 / 60%); background: rgb(0 0 0 / 60%);
opacity: 0; opacity: 0;
box-sizing: border-box;
transition: var(--el-transition-duration-fast); transition: var(--el-transition-duration-fast);
align-items: center;
justify-content: center;
.handle-icon { .handle-icon {
display: flex; display: flex;

@ -0,0 +1,93 @@
import * as FileApi from '@/api/infra/file'
import CryptoJS from 'crypto-js'
import { UploadRawFile, UploadRequestOptions } from 'element-plus/es/components/upload/src/upload'
import axios from 'axios'
export const useUpload = () => {
// 后端上传地址
const uploadUrl = import.meta.env.VITE_UPLOAD_URL
// 是否使用前端直连上传
const isClientUpload = UPLOAD_TYPE.CLIENT === import.meta.env.VITE_UPLOAD_TYPE
// 重写ElUpload上传方法
const httpRequest = async (options: UploadRequestOptions) => {
// 模式一:前端上传
if (isClientUpload) {
// 1.1 生成文件名称
const fileName = await generateFileName(options.file)
// 1.2 获取文件预签名地址
const presignedInfo = await FileApi.getFilePresignedUrl(fileName)
// 1.3 上传文件(不能使用 ElUpload 的 ajaxUpload 方法的原因:其使用的是 FormData 上传,Minio 不支持)
return axios.put(presignedInfo.uploadUrl, options.file).then(() => {
// 1.4. 记录文件信息到后端(异步)
createFile(presignedInfo, fileName, options.file)
// 通知成功,数据格式保持与后端上传的返回结果一致
return { data: presignedInfo.url }
})
} else {
// 模式二:后端上传
// 重写 el-upload httpRequest 文件上传成功会走成功的钩子,失败走失败的钩子
return new Promise((resolve, reject) => {
FileApi.updateFile({ file: options.file })
.then((res) => {
if (res.code === 0) {
resolve(res)
} else {
reject(res)
}
})
.catch((res) => {
reject(res)
})
})
}
}
return {
uploadUrl,
httpRequest
}
}
/**
*
* @param vo
* @param name
* @param file
*/
function createFile(vo: FileApi.FilePresignedUrlRespVO, name: string, file: UploadRawFile) {
const fileVo = {
configId: vo.configId,
url: vo.url,
path: name,
name: file.name,
type: file.type,
size: file.size
}
FileApi.createFile(fileVo)
return fileVo
}
/**
* 使SHA256
* @param file
*/
async function generateFileName(file: UploadRawFile) {
// 读取文件内容
const data = await file.arrayBuffer()
const wordArray = CryptoJS.lib.WordArray.create(data)
// 计算SHA256
const sha256 = CryptoJS.SHA256(wordArray).toString()
// 拼接后缀
const ext = file.name.substring(file.name.lastIndexOf('.'))
return `${sha256}${ext}`
}
/**
*
*/
enum UPLOAD_TYPE {
// 客户端直接上传(只支持S3服务)
CLIENT = 'client',
// 客户端发送到后端上传
SERVER = 'server'
}

@ -58,7 +58,7 @@
</el-row> </el-row>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="24" :offset="0"> <el-col :span="24" :offset="0">
<el-form-item label="状态" prop="remark"> <el-form-item label="备注" prop="remark">
<Editor v-model:modelValue="formData.remark" /> <Editor v-model:modelValue="formData.remark" />
</el-form-item> </el-form-item>
</el-col> </el-col>

@ -1,12 +1,12 @@
<template> <template>
<div> <div>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<Search <Search :schema="allSchemas.searchSchema" labelWidth="0">
:schema="allSchemas.searchSchema" <template #actionMore>
labelWidth="0" <el-button @click="getTableList" v-hasPermi="['clue:order:search']"> 搜索 </el-button>
@search="setSearchParams" <el-button @click="resetQuery" v-hasPermi="['clue:order:reset']"> 重置 </el-button>
@reset="setSearchParams" </template>
/> </Search>
<!-- 列表 --> <!-- 列表 -->
<SSTable <SSTable
class="mt-20px" class="mt-20px"
@ -21,30 +21,55 @@
:label="item.label" :label="item.label"
min-width="120px" min-width="120px"
/> />
<el-table-column label="操作" width="140px" fixed="right"> <el-table-column label="操作" width="200px" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button <el-button
type="primary" type="primary"
class="mr-10px" class="mr-10px"
link link
style="padding: 0; margin-left: 0" style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:after-sale']"
@click="sellAfter(scope.row)" @click="sellAfter(scope.row)"
>售后</el-button
> >
<el-button type="primary" class="mr-10px" link style="padding: 0; margin-left: 0" 售后
>售后审核</el-button </el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:after-sale-audit']"
>
售后审核
</el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:send']"
> >
发货(进销存)
</el-button>
<el-button <el-button
type="primary" type="primary"
class="mr-10px" class="mr-10px"
link link
style="padding: 0; margin-left: 0" style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:return']"
@click="feeBack(scope.row)" @click="feeBack(scope.row)"
>回款</el-button
> >
<el-button type="primary" class="mr-10px" link style="padding: 0; margin-left: 0" 回款
>回款确认</el-button </el-button>
<el-button
type="primary"
class="mr-10px"
link
style="padding: 0; margin-left: 0"
v-hasPermi="['clue:order:return-audit']"
> >
回款确认
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</SSTable> </SSTable>
@ -62,7 +87,7 @@ const tableObject = ref({
currentPage: 1 currentPage: 1
}) })
function setSearchParams() { function resetQuery() {
// //
} }
// //

@ -30,17 +30,19 @@
<el-tab-pane label="公海" name="4" /> <el-tab-pane label="公海" name="4" />
</el-tabs> </el-tabs>
<div class="absolute" style="right: 10px; top: 0"> <div class="absolute" style="right: 10px; top: 0">
<el-button plain>导入</el-button> <el-button plain v-hasPermi="['clue:pool:import']">导入</el-button>
<el-button type="primary" @click="handleInsert">新增线索</el-button> <el-button type="primary" @click="handleInsert" v-hasPermi="['clue:pool:add']">
新增线索
</el-button>
</div> </div>
</div> </div>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<Search <Search :schema="allSchemas.searchSchema" labelWidth="0">
:schema="allSchemas.searchSchema" <template #actionMore>
labelWidth="0" <el-button @click="getTableList" v-hasPermi="['clue:pool:search']"> 搜索 </el-button>
@search="setSearchParams" <el-button @click="resetQuery" v-hasPermi="['clue:pool:reset']"> 重置 </el-button>
@reset="setSearchParams" </template>
/> </Search>
<!-- 列表 --> <!-- 列表 -->
<SSTable <SSTable
class="mt-20px" class="mt-20px"
@ -58,9 +60,15 @@
> >
<template #default="{ row }"> <template #default="{ row }">
<div v-if="item.field == 'followRecord'"> <div v-if="item.field == 'followRecord'">
<el-button type="primary" text style="padding: 0" @click="handleFollow(row)" <el-button
>快速新增</el-button type="primary"
text
style="padding: 0"
@click="handleFollow(row)"
v-hasPermi="['clue:pool:update']"
> >
快速新增
</el-button>
</div> </div>
<div v-else-if="item.field == 'contact'"> <div v-else-if="item.field == 'contact'">
<span>{{ row[item.field] }}</span> <span>{{ row[item.field] }}</span>
@ -71,10 +79,31 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" width="200px" fixed="right"> <el-table-column label="操作" width="200px" fixed="right">
<template #default="scope"> <template #default="scope">
<el-button type="primary" link @click="handleDetail(scope.row)">详情</el-button> <el-button
<el-button type="primary" link @click="handleEdit(scope.row)">修改</el-button> type="primary"
<el-button type="primary" link @click="handleSuccess(scope.row)">登记</el-button> link
<el-button type="primary" link>释放</el-button> @click="handleDetail(scope.row)"
v-hasPermi="['clue:pool:detail']"
>
详情
</el-button>
<el-button
type="primary"
link
@click="handleEdit(scope.row)"
v-hasPermi="['clue:pool:update']"
>
修改
</el-button>
<el-button
type="primary"
link
@click="handleSuccess(scope.row)"
v-hasPermi="['clue:pool:enroll']"
>
登记
</el-button>
<el-button type="primary" link v-hasPermi="['clue:pool:release']"> 释放 </el-button>
</template> </template>
</el-table-column> </el-table-column>
</SSTable> </SSTable>
@ -121,8 +150,12 @@ function getCheckedColumns(list) {
showColumns.value = list showColumns.value = list
} }
const setSearchParams = function () { function resetQuery() {
// searchForm.value = {
pageNo: 1,
pageSize: 10
}
getTableList()
} }
function getTableList() { function getTableList() {

@ -0,0 +1,50 @@
<template>
<el-form :model="form" ref="formRef" label-width="auto">
<el-form-item label="售后申请自动通过">
<el-radio-group v-model="form.autoAudiAfterSale">
<el-radio :label="0"> </el-radio>
<el-radio :label="1"> </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="发货自动发起采购">
<el-radio-group v-model="form.autoSend">
<el-radio :label="0"> </el-radio>
<el-radio :label="1"> </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="回款申请自动通过">
<el-radio-group v-model="form.autoAuditReturn">
<el-radio :label="0"> </el-radio>
<el-radio :label="1"> </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">保存</el-button>
<el-button @click="getData">刷新</el-button>
</el-form-item>
</el-form>
</template>
<script setup name="GeneralClue">
const message = useMessage()
const form = ref({
autoAudiAfterSale: 0,
autoSend: 0,
autoAuditReturn: 0
})
function getData() {
form.value = {
autoAudiAfterSale: 0,
autoSend: 0,
autoAuditReturn: 0
}
}
function onSubmit() {
message.success('保存成功')
}
</script>
<style lang="scss" scoped></style>

@ -16,7 +16,10 @@
<el-tab-pane label="线索分配规则" :name="30"> <el-tab-pane label="线索分配规则" :name="30">
<ClueSend /> <ClueSend />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="消息通知" :name="40"> <el-tab-pane label="常规设置" :name="40">
<GeneralSet />
</el-tab-pane>
<el-tab-pane label="消息通知" :name="50">
<MsgSend /> <MsgSend />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -30,6 +33,7 @@ import ClueSource from './Comp/ClueSource.vue'
import ClueGet from './Comp/ClueGet.vue' import ClueGet from './Comp/ClueGet.vue'
import ClueSend from './Comp/ClueSend.vue' import ClueSend from './Comp/ClueSend.vue'
import MsgSend from './Comp/MsgSend.vue' import MsgSend from './Comp/MsgSend.vue'
import GeneralSet from './Comp/GeneralSet.vue'
const tabIndex = ref(0) const tabIndex = ref(0)
</script> </script>

@ -1,12 +1,12 @@
<template> <template>
<el-tabs v-model="curTab" type="card" tab-position="top"> <el-tabs v-model="curTab" type="card" tab-position="top">
<el-tab-pane label="库存" name="1"> <el-tab-pane label="库存" name="1" v-if="checkPermi(['mall:inventory:index'])">
<InventoryDetail v-if="curTab == 1" /> <InventoryDetail v-if="curTab == 1" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="库存变动记录" name="2"> <el-tab-pane label="库存变动记录" name="2" v-if="checkPermi(['mall:inventory:record'])">
<InventoryRecord v-if="curTab == 2" /> <InventoryRecord v-if="curTab == 2" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="仓库" name="3"> <el-tab-pane label="仓库" name="3" v-if="checkPermi(['mall:inventory:depot'])">
<Warehouse v-if="curTab == 3" /> <Warehouse v-if="curTab == 3" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -16,6 +16,7 @@
import InventoryDetail from './Comp/InventoryDetail.vue' import InventoryDetail from './Comp/InventoryDetail.vue'
import Warehouse from './Comp/Warehouse.vue' import Warehouse from './Comp/Warehouse.vue'
import InventoryRecord from './Comp/InventoryRecord.vue' import InventoryRecord from './Comp/InventoryRecord.vue'
import { checkPermi } from '@/utils/permission'
const curTab = ref('1') const curTab = ref('1')
</script> </script>

@ -15,22 +15,31 @@
<el-button type="primary" @click="openForm('create', null)">新增</el-button> <el-button type="primary" @click="openForm('create', null)">新增</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table v-loading="loading" :data="tableList"> <el-table v-loading="loading" :data="list">
<el-table-column prop="brandName" label="品牌名称" /> <el-table-column prop="name" label="品牌名称" />
<el-table-column prop="orderNum" label="排序" width="100px" /> <el-table-column prop="sort" label="排序" width="100px" />
<el-table-column prop="remark" label="备注" /> <el-table-column prop="description" label="备注" />
<el-table-column label="创建时间" prop="createTime" width="180px" /> <el-table-column label="开启状态" prop="status">
<el-table-column label="创建人" prop="createUser" width="150px" /> <template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
prop="createTime"
width="180px"
:formatter="dateFormatter"
/>
<el-table-column label="操作"> <el-table-column label="操作">
<template #default="scope"> <template #default="scope">
<el-button type="primary" text @click="openForm('update', scope.row)">修改</el-button> <el-button type="primary" text @click="openForm('update', scope.row.id)">修改</el-button>
<el-button type="danger" text @click="handleDelete(scope.row)">删除</el-button> <el-button type="danger" text @click="handleDelete(scope.row.id)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<Pagination <Pagination
v-model:limit="searchForm.pageSize" v-model:limit="searchForm.pageSize"
v-model:page="searchForm.pageNum" v-model:page="searchForm.pageNo"
:total="total" :total="total"
@pagination="handleQuery" @pagination="handleQuery"
/> />
@ -40,51 +49,56 @@
</template> </template>
<script setup name="BrandSet"> <script setup name="BrandSet">
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import * as ProductBrandApi from '@/api/mall/product/brand'
import DialogBrand from './DialogBrand.vue' import DialogBrand from './DialogBrand.vue'
const searchForm = ref({ const searchForm = ref({
pageNum: 1, name: undefined,
pageNo: 1,
pageSize: 20 pageSize: 20
}) })
const total = ref(0) const total = ref(0)
const brandDialog = ref() const brandDialog = ref()
const tableList = ref([]) const list = ref([])
const loading = ref(false) const loading = ref(false)
function handleQuery() { function handleQuery() {
searchForm.value.pageNum = 1 searchForm.value.pageNo = 1
getList() getList()
} }
function resetQuery() { function resetQuery() {
searchForm.value = { searchForm.value = {
name: '', name: '',
pageSize: 20, pageSize: 20,
pageNum: 1 pageNo: 1
} }
getList() getList()
} }
function getList() { async function getList() {
tableList.value = [ loading.value = true
{ try {
brandId: 1, const data = await ProductBrandApi.getBrandParam(searchForm.value)
brandName: '测试' list.value = data.list
} total.value = data.total
] } finally {
loading.value = false
}
} }
function openForm(type, info) { function openForm(type, id) {
brandDialog.value.open(type, info) brandDialog.value.open(type, id)
} }
async function handleDelete(row) { async function handleDelete(id) {
try { try {
console.log(row)
// //
await message.delConfirm() await message.delConfirm()
// //
// await UserApi.deleteUser(row.id) await ProductBrandApi.deleteBrand(id)
message.success(t('common.delSuccess')) message.success(t('common.delSuccess'))
// //
await getList() await getList()

@ -15,24 +15,28 @@
<el-button type="primary" @click="openForm('create', null)">新增</el-button> <el-button type="primary" @click="openForm('create', null)">新增</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table <el-table v-loading="loading" :data="list" row-key="id" :tree-props="{ children: 'children' }">
v-loading="loading"
:data="tableList"
row-key="categoryId"
:tree-props="{ children: 'children' }"
>
<el-table-column prop="categoryName" label="分类名称" /> <el-table-column prop="categoryName" label="分类名称" />
<el-table-column prop="orderNum" label="排序" width="100px" /> <el-table-column prop="orderNum" label="排序" width="100px" />
<el-table-column prop="remark" label="备注" /> <el-table-column prop="remark" label="备注" />
<el-table-column label="创建时间" prop="createTime" width="180px" /> <el-table-column label="状态" align="center" min-width="150" prop="status">
<el-table-column label="创建人" prop="createUser" width="150px" /> <template #default="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
label="创建时间"
prop="createTime"
width="180px"
:formatter="dateFormatter"
/>
<el-table-column label="操作"> <el-table-column label="操作">
<template #default="scope"> <template #default="scope">
<el-button type="primary" text @click="openForm('update', scope.row)">修改</el-button> <el-button type="primary" text @click="openForm('update', scope.row)">修改</el-button>
<el-button type="primary" text @click="openForm('createChildren', scope.row)" <el-button type="primary" text @click="openForm('createChildren', scope.row)"
>新增</el-button >新增</el-button
> >
<el-button type="danger" text @click="handleDelete(scope.row)">删除</el-button> <el-button type="danger" text @click="handleDelete(scope.row.id)">删除</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -48,58 +52,61 @@
</template> </template>
<script setup name="CategorySet"> <script setup name="CategorySet">
import * as ProductCategoryApi from '@/api/mall/product/category'
import { handleTree } from '@/utils/tree'
import DialogCategory from './DialogCategory.vue' import DialogCategory from './DialogCategory.vue'
import { dateFormatter } from '@/utils/formatTime'
import { DICT_TYPE } from '@/utils/dict'
const message = useMessage() //
const { t } = useI18n() //
const searchForm = ref({ const searchForm = ref({
pageNum: 1, name: undefined
pageSize: 20
}) })
const total = ref(0) const total = ref(0)
const categoryDialog = ref() const categoryDialog = ref()
const tableList = ref([]) const list = ref([])
const loading = ref(false) const loading = ref(false)
function handleQuery() { function handleQuery() {
searchForm.value.pageNum = 1
getList() getList()
} }
function resetQuery() { function resetQuery() {
searchForm.value = { searchForm.value = {
name: '', name: undefined
pageSize: 20,
pageNum: 1
} }
getList() getList()
} }
function getList() { async function getList() {
tableList.value = [ loading.value = true
{ try {
categoryId: 1, const data = await ProductCategoryApi.getCategoryList(searchForm.value)
categoryName: '测试', list.value = handleTree(data, 'id', 'parentId')
level: 1, } finally {
children: [{ categoryId: 1001, categoryName: '二级分类', level: 2, parentCategory: '测试' }] loading.value = false
} }
]
} }
function openForm(type, info) { function openForm(type, info) {
categoryDialog.value.open(type, info) categoryDialog.value.open(type, info)
} }
async function handleDelete(row) { async function handleDelete(id) {
try { try {
console.log(row)
// //
await message.delConfirm() await message.delConfirm()
// //
// await UserApi.deleteUser(row.id) await ProductCategoryApi.deleteCategory(id)
message.success(t('common.delSuccess')) message.success(t('common.delSuccess'))
// //
await getList() await getList()
} catch {} } catch {}
} }
handleQuery()
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>

@ -1,5 +1,5 @@
<template> <template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px"> <Dialog :title="dialogTitle" v-model="dialogVisible" width="500px">
<el-form <el-form
ref="formRef" ref="formRef"
v-loading="formLoading" v-loading="formLoading"
@ -7,11 +7,17 @@
:rules="formRules" :rules="formRules"
label-width="80px" label-width="80px"
> >
<el-form-item label="品牌名称" prop="brandName"> <el-form-item label="品牌名称" prop="name">
<el-input v-model="formData.brandName" placeholder="请输入品牌名称" /> <el-input v-model="formData.name" placeholder="请输入品牌名称" />
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="orderNum"> <el-form-item label="排序" prop="sort">
<el-input v-model="formData.orderNum" placeholder="请输入排序" type="number" :min="0" /> <el-input v-model="formData.sort" placeholder="请输入排序" type="number" :min="0" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="0"> 启用 </el-radio>
<el-radio :label="1"> 禁用 </el-radio>
</el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input <el-input
@ -29,6 +35,8 @@
</Dialog> </Dialog>
</template> </template>
<script name="DialogBrand" setup> <script name="DialogBrand" setup>
import * as ProductBrandApi from '@/api/mall/product/brand'
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -37,27 +45,27 @@ const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 12 const formLoading = ref(false) // 12
const formType = ref('') // create - update - const formType = ref('') // create - update -
const formData = ref({ const formData = ref({
brandName: '', name: '',
orderNum: 1, sort: 1,
status: 0,
remark: '' remark: ''
}) })
const formRules = reactive({ const formRules = reactive({
brandName: [{ required: true, message: '名称不能为空', trigger: 'blur' }] name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
/** 打开弹窗 */ /** 打开弹窗 */
const open = async (type, info) => { const open = async (type, id) => {
dialogVisible.value = true dialogVisible.value = true
dialogTitle.value = type == 'update' ? '修改品牌' : '新增品牌' dialogTitle.value = type == 'update' ? '修改品牌' : '新增品牌'
formType.value = type formType.value = type
resetForm() resetForm()
// //
if (info.brandId) { if (id) {
formLoading.value = true formLoading.value = true
try { try {
formData.value = { ...info } formData.value = await ProductBrandApi.getBrand(id)
// formData.value = await UserApi.getUser(id)
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
@ -75,12 +83,11 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
// const data = formData.value as unknown as UserApi.UserVO
if (formType.value === 'create') { if (formType.value === 'create') {
// await UserApi.createUser(data) await ProductBrandApi.createBrand(formData.value)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
} else { } else {
// await UserApi.updateUser(data) await ProductBrandApi.updateBrand(formData.value)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
} }
dialogVisible.value = false dialogVisible.value = false
@ -94,8 +101,9 @@ const submitForm = async () => {
/** 重置表单 */ /** 重置表单 */
const resetForm = () => { const resetForm = () => {
formData.value = { formData.value = {
brandName: '', name: '',
orderNum: 1, sort: 1,
status: 0,
remark: '' remark: ''
} }
formRef.value?.resetFields() formRef.value?.resetFields()

@ -1,5 +1,5 @@
<template> <template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px"> <Dialog :title="dialogTitle" v-model="dialogVisible" width="500px">
<el-form <el-form
ref="formRef" ref="formRef"
v-loading="formLoading" v-loading="formLoading"
@ -10,11 +10,17 @@
<el-form-item v-if="formData.level > 1" label="上级分类"> <el-form-item v-if="formData.level > 1" label="上级分类">
<el-input v-model="formData.parentCategory" disabled /> <el-input v-model="formData.parentCategory" disabled />
</el-form-item> </el-form-item>
<el-form-item label="分类名称" prop="categoryName"> <el-form-item label="分类名称" prop="name">
<el-input v-model="formData.categoryName" placeholder="请输入分类名称" /> <el-input v-model="formData.name" placeholder="请输入分类名称" />
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="orderNum"> <el-form-item label="排序" prop="sort">
<el-input v-model="formData.orderNum" placeholder="请输入排序" type="number" :min="0" /> <el-input v-model="formData.sort" placeholder="请输入排序" type="number" :min="0" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio :label="0"> 启用 </el-radio>
<el-radio :label="1"> 禁用 </el-radio>
</el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input <el-input
@ -32,6 +38,8 @@
</Dialog> </Dialog>
</template> </template>
<script name="DialogCategory" setup> <script name="DialogCategory" setup>
import * as ProductCategoryApi from '@/api/mall/product/category'
const { t } = useI18n() // const { t } = useI18n() //
const message = useMessage() // const message = useMessage() //
@ -40,12 +48,13 @@ const dialogTitle = ref('') // 弹窗的标题
const formLoading = ref(false) // 12 const formLoading = ref(false) // 12
const formType = ref('') // create - update - const formType = ref('') // create - update -
const formData = ref({ const formData = ref({
categoryName: '', name: '',
orderNum: 1, sort: 1,
status: 0,
remark: '' remark: ''
}) })
const formRules = reactive({ const formRules = reactive({
categoryName: [{ required: true, message: '名称不能为空', trigger: 'blur' }] name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
}) })
const formRef = ref() // Ref const formRef = ref() // Ref
@ -56,16 +65,15 @@ const open = async (type, info) => {
formType.value = type formType.value = type
resetForm() resetForm()
// //
if (info.categoryId) { if (info?.id) {
formLoading.value = true formLoading.value = true
try { try {
if (type == 'update') { if (type == 'update') {
formData.value = { ...info } formData.value = await ProductCategoryApi.getCategory(info.id)
} else { } else {
formData.value.level = info.level + 1 formData.value.level = info.level + 1
formData.value.parentCategory = info.categoryName formData.value.parentCategory = info.name
} }
// formData.value = await UserApi.getUser(id)
} finally { } finally {
formLoading.value = false formLoading.value = false
} }
@ -83,12 +91,11 @@ const submitForm = async () => {
// //
formLoading.value = true formLoading.value = true
try { try {
// const data = formData.value as unknown as UserApi.UserVO
if (formType.value === 'create') { if (formType.value === 'create') {
// await UserApi.createUser(data) await ProductCategoryApi.createCategory(formData.value)
message.success(t('common.createSuccess')) message.success(t('common.createSuccess'))
} else { } else {
// await UserApi.updateUser(data) await ProductCategoryApi.updateCategory(data)
message.success(t('common.updateSuccess')) message.success(t('common.updateSuccess'))
} }
dialogVisible.value = false dialogVisible.value = false
@ -102,8 +109,9 @@ const submitForm = async () => {
/** 重置表单 */ /** 重置表单 */
const resetForm = () => { const resetForm = () => {
formData.value = { formData.value = {
categoryName: '', name: '',
orderNum: 1, sort: 1,
status: 0,
remark: '' remark: ''
} }
formRef.value?.resetFields() formRef.value?.resetFields()

@ -1,22 +1,22 @@
<template> <template>
<el-tabs v-model="tabIndex" type="border-card"> <el-tabs v-model="tabIndex" type="border-card">
<el-tab-pane label="产品属性" :name="0"> <el-tab-pane label="产品属性" :name="0" v-if="checkPermi(['mall:setting:prod'])">
<FieldProduct /> <FieldProduct v-if="tabIndex == 0" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="分类设置" :name="1"> <el-tab-pane label="分类设置" :name="1" v-if="checkPermi(['mall:setting:category'])">
<CategorySet /> <CategorySet v-if="tabIndex == 1" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="品牌设置" :name="2"> <el-tab-pane label="品牌设置" :name="2" v-if="checkPermi(['mall:setting:brand'])">
<BrandSet /> <BrandSet v-if="tabIndex == 2" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="供应商设置" :name="3"> <el-tab-pane label="供应商设置" :name="3" v-if="checkPermi(['mall:setting:supplier'])">
<SupplierSet /> <SupplierSet v-if="tabIndex == 3" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="常规设置" :name="4"> <el-tab-pane label="常规设置" :name="4" v-if="checkPermi(['mall:setting:general'])">
<GeneralSet /> <GeneralSet v-if="tabIndex == 4" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="消息通知" :name="9"> <el-tab-pane label="消息通知" :name="9" v-if="checkPermi(['mall:setting:msg'])">
<MsgSend /> <MsgSend v-if="tabIndex == 9" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</template> </template>
@ -28,6 +28,7 @@ import MsgSend from './Comp/MsgSend.vue'
import CategorySet from './Comp/CategorySet.vue' import CategorySet from './Comp/CategorySet.vue'
import BrandSet from './Comp/BrandSet.vue' import BrandSet from './Comp/BrandSet.vue'
import SupplierSet from './Comp/SupplierSet.vue' import SupplierSet from './Comp/SupplierSet.vue'
import { checkPermi } from '@/utils/permission'
const tabIndex = ref(0) const tabIndex = ref(0)
</script> </script>

@ -16,6 +16,7 @@
placeholder="请选择分类" placeholder="请选择分类"
filterable filterable
show-all-levels show-all-levels
style="width: 100%"
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -44,15 +45,15 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12" :offset="0"> <el-col :span="12" :offset="0">
<el-form-item label="主图" prop="picUrl"> <el-form-item label="主图" prop="mainImage">
<UploadImg v-model="form.picUrl" height="100px" width="100px" /> <UploadImg v-model="form.mainImage" height="100px" width="100px" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="24" :offset="0"> <el-col :span="24" :offset="0">
<el-form-item label="轮播图" prop="sliderPicUrls"> <el-form-item label="轮播图" prop="carouselImages">
<UploadImgs v-model:modelValue="form.sliderPicUrls" height="100px" width="100px" /> <UploadImgs v-model="form.carouselImages" height="100px" width="100px" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@ -60,7 +61,7 @@
<el-col :span="24" :offset="0"> <el-col :span="24" :offset="0">
<el-form-item label="商品规格"> <el-form-item label="商品规格">
<el-button @click="handleAddSpec">添加规格</el-button> <el-button @click="handleAddSpec">添加规格</el-button>
<el-col v-for="(item, index) in form.specsList" :key="index"> <el-col v-for="(item, index) in form.productSpecList" :key="index">
<div> <div>
<el-text class="mx-1">属性名</el-text> <el-text class="mx-1">属性名</el-text>
<el-tag class="mx-1" closable type="success" @close="handleCloseProperty(index)" <el-tag class="mx-1" closable type="success" @close="handleCloseProperty(index)"
@ -109,7 +110,7 @@
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
v-for="col in form.specsList" v-for="col in form.productSpecList"
:prop="col.id" :prop="col.id"
:key="col.id" :key="col.id"
:label="col.name" :label="col.name"
@ -135,10 +136,10 @@
</div> </div>
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="详细信息" name="detail"> <el-tab-pane label="详细信息" name="detail">
<Editor v-model:modelValue="form.description" /> <Editor v-model:modelValue="form.detailInfo" />
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
<ProductAttributesAddForm ref="attributesAddFormRef" :propertyList="form.specsList" /> <ProductAttributesAddForm ref="attributesAddFormRef" :propertyList="form.productSpecList" />
</template> </template>
<script setup> <script setup>
@ -148,27 +149,33 @@ const message = useMessage() // 消息弹窗
const tabName = ref('basic') const tabName = ref('basic')
const form = ref({ const form = ref({
name: '', productName: undefined,
category: '', productCategory: undefined,
brand: '', productBrand: undefined,
intro: '', productIntro: undefined,
picUrl: '', mainImage: '',
sliderPicUrls: [], carouselImages: [],
specsList: [], productSpecList: [],
skuList: [], skuList: [],
description: null detailInfo: null
}) })
const rules = ref({}) const rules = ref({})
const attributesAddFormRef = ref() // const attributesAddFormRef = ref() //
const opts = { const opts = {
brand: [], brand: [{ value: 1, label: '品牌1' }],
productCategory: [] productCategory: [
{
id: 1,
label: '分类1',
children: [{ id: 2, label: '子分类1' }]
}
]
} }
/** 删除属性*/ /** 删除属性*/
function handleCloseProperty(index) { function handleCloseProperty(index) {
form.value.specsList?.splice(index, 1) form.value.productSpecList?.splice(index, 1)
} }
const attributeIndex = ref(null) const attributeIndex = ref(null)
@ -186,7 +193,7 @@ async function handleInputConfirm(index, propertyId) {
try { try {
// const id = await PropertyApi.createPropertyValue({ propertyId, name: inputValue.value }) // const id = await PropertyApi.createPropertyValue({ propertyId, name: inputValue.value })
const id = propertyId || parseInt(Math.random() * 1000000) const id = propertyId || parseInt(Math.random() * 1000000)
form.value.specsList[index].values.push({ id, name: inputValue.value }) form.value.productSpecList[index].values.push({ id, name: inputValue.value })
message.success('添加成功') message.success('添加成功')
} catch { } catch {
message.error('添加失败,请重试') message.error('添加失败,请重试')
@ -199,7 +206,7 @@ async function handleInputConfirm(index, propertyId) {
function getTableList() { function getTableList() {
let list = [] let list = []
form.value.specsList.map((item) => { form.value.productSpecList.map((item) => {
if (!list.length) { if (!list.length) {
item.values.map((it) => { item.values.map((it) => {
const obj = {} const obj = {}
@ -238,8 +245,7 @@ const setInputRef = (el) => {
} }
function handleAddSpec() { function handleAddSpec() {
const id = parseInt(Math.random() * 1000) attributesAddFormRef.value.open()
form.value.specsList.push({ name: `测试规格${id}`, id, values: [] })
} }
function onSubmit() { function onSubmit() {
@ -249,19 +255,19 @@ function onSubmit() {
onMounted(() => { onMounted(() => {
if (route.query?.id) { if (route.query?.id) {
form.value = { form.value = {
name: '商品名称哦~' productName: '商品名称哦~'
} }
} else { } else {
form.value = { form.value = {
name: '', productName: undefined,
category: '', productCategory: undefined,
brand: '', productBrand: undefined,
intro: '', productIntro: undefined,
picUrl: '', mainImage: 'https://ss-cloud.ahduima.com/1001/1796426117251600384.png',
sliderPicUrls: [], carouselImages: ['https://ss-cloud.ahduima.com/1001/1796426117251600384.png'],
specsList: [], productSpecList: [],
skuList: [], skuList: [],
description: null detailInfo: null
} }
} }
}) })

@ -38,9 +38,11 @@
/> />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> 搜索 </el-button> <el-button @click="handleQuery" v-hasPermi="['mall:prod:search']"> 搜索 </el-button>
<el-button @click="resetQuery"> 重置 </el-button> <el-button @click="resetQuery" v-hasPermi="['mall:prod:reset']"> 重置 </el-button>
<el-button plain type="primary" @click="openForm"> 新增 </el-button> <el-button plain type="primary" @click="openForm" v-hasPermi="['mall:prod:add']">
新增
</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -71,8 +73,22 @@
<el-table-column fixed="right" label="操作" min-width="80"> <el-table-column fixed="right" label="操作" min-width="80">
<template #default="{ row }"> <template #default="{ row }">
<!-- TODO详情可以后面点做哈 --> <!-- TODO详情可以后面点做哈 -->
<el-button link type="primary" @click="openForm(row.productId)"> 修改 </el-button> <el-button
<el-button link type="danger" @click="handleDelete(row.productId)"> 删除 </el-button> link
type="primary"
@click="openForm(row.productId)"
v-hasPermi="['mall:prod:update']"
>
修改
</el-button>
<el-button
link
type="danger"
@click="handleDelete(row.productId)"
v-hasPermi="['mall:prod:delete']"
>
删除
</el-button>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>

@ -1,16 +1,13 @@
<template> <template>
<div> <div>
<!-- 搜索工作栏 --> <!-- 搜索工作栏 -->
<Search <Search :schema="allSchemas.searchSchema" labelWidth="0">
:schema="allSchemas.searchSchema"
labelWidth="0"
expand
expand-field="supplier"
@search="setSearchParams"
@reset="setSearchParams"
>
<template #actionMore> <template #actionMore>
<el-button type="primary" @click="handleAdd">发起采购</el-button> <el-button @click="getList" v-hasPermi="['mall:purchase:search']"> 搜索 </el-button>
<el-button @click="resetQuery" v-hasPermi="['mall:purchase:reset']"> 重置 </el-button>
<el-button type="primary" @click="handleAdd" v-hasPermi="['mall:purchase:add']">
发起采购
</el-button>
</template> </template>
</Search> </Search>
<!-- 列表 --> <!-- 列表 -->
@ -31,11 +28,32 @@
/> />
<el-table-column label="操作" width="150px" fixed="right"> <el-table-column label="操作" width="150px" fixed="right">
<template #default="{ row }"> <template #default="{ row }">
<el-button type="primary" v-if="row.status == 1" link @click="handleAudit(row)" <el-button
>采购审核</el-button type="primary"
v-if="row.status == 1"
link
@click="handleAudit(row)"
v-hasPermi="['mall:purchase:audit']"
>
采购审核
</el-button>
<el-button
v-else
type="primary"
link
@click="purchaseAgain(row)"
v-hasPermi="['mall:purchase:readd']"
>
再次采购
</el-button>
<el-button
type="primary"
link
@click="handleDetail(row)"
v-hasPermi="['mall:purchase:detail']"
> >
<el-button v-else type="primary" link @click="purchaseAgain(row)">再次采购</el-button> 详情
<el-button type="primary" link @click="handleDetail(row)">详情</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
</SSTable> </SSTable>
@ -59,7 +77,19 @@ const tableObject = ref({
pageSize: 20, pageSize: 20,
currentPage: 1 currentPage: 1
}) })
const setSearchParams = function () {
function resetQuery() {
queryParams.value = {
productName: undefined,
productBrand: undefined,
productCategory: undefined,
pageNo: 1,
pageSize: 10
}
getList()
}
const getList = function () {
tableObject.value.tableList = [ tableObject.value.tableList = [
{ name: '测试', status: 1, statusName: '审核中', supplier: '林氏木业', purchaseCount: 10 }, { name: '测试', status: 1, statusName: '审核中', supplier: '林氏木业', purchaseCount: 10 },
{ name: '测试2', status: 2, statusName: '已通过', supplier: '张氏木业', purchaseCount: 1 }, { name: '测试2', status: 2, statusName: '已通过', supplier: '张氏木业', purchaseCount: 1 },

Loading…
Cancel
Save