Merge branch 'main' of http://114.55.169.15:3000/qiushanhe/ss-crm-manage-web into dev-cl
This commit is contained in:
@@ -8,7 +8,7 @@ VITE_DEV=true
|
|||||||
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
|
||||||
|
|||||||
2
.env.dev
2
.env.dev
@@ -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
|
||||||
|
|||||||
2
.env.pro
2
.env.pro
@@ -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=
|
||||||
|
|||||||
45
src/api/infra/file/index.ts
Normal file
45
src/api/infra/file/index.ts
Normal file
@@ -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,30 +32,30 @@ 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 })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得商品品牌精简信息列表
|
// 获得商品品牌精简信息列表
|
||||||
export const getSimpleBrandList = () => {
|
export const getSimpleBrandList = () => {
|
||||||
return request.get({ url: '/product/brand/list-all-simple' })
|
return request.get({ url: '/admin-api/crm/erp-product-brand/simple-list' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,25 +36,30 @@ 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/list', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得商品分类列表
|
||||||
|
export const getCategorySimpleList = (params: any) => {
|
||||||
|
return request.get({ url: '/admin-api/crm/erp-product-category/simple-list', params })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
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 })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSimpleProductList = async () => {
|
||||||
|
return await request.get({ url: '/admin-api/crm/erp-product/simple-list' })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export interface PropertyValueDetailVO {
|
|||||||
|
|
||||||
// 创建属性项
|
// 创建属性项
|
||||||
export const createProperty = (data: PropertyVO) => {
|
export const createProperty = (data: PropertyVO) => {
|
||||||
return request.post({ url: '/product/property/create', data })
|
return request.post({ url: '/admin-api/crm/erp-product-property/create', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新属性项
|
// 更新属性项
|
||||||
@@ -89,7 +89,7 @@ export const getPropertyValue = (id: number): Promise<PropertyValueVO> => {
|
|||||||
|
|
||||||
// 创建属性值
|
// 创建属性值
|
||||||
export const createPropertyValue = (data: PropertyValueVO) => {
|
export const createPropertyValue = (data: PropertyValueVO) => {
|
||||||
return request.post({ url: '/product/property/value/create', data })
|
return request.post({ url: '/admin-api/crm/erp-product-property-value/create', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新属性值
|
// 更新属性值
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
import request from '@/config/axios'
|
|
||||||
|
|
||||||
export interface Property {
|
|
||||||
propertyId?: number // 属性编号
|
|
||||||
propertyName?: string // 属性名称
|
|
||||||
valueId?: number // 属性值编号
|
|
||||||
valueName?: string // 属性值名称
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO puhui999:是不是直接叫 Sku 更简洁一点哈。type 待后面,总感觉有个类型?
|
|
||||||
export interface SkuType {
|
|
||||||
id?: number // 商品 SKU 编号
|
|
||||||
spuId?: number // SPU 编号
|
|
||||||
properties?: Property[] // 属性数组
|
|
||||||
price?: number // 商品价格
|
|
||||||
marketPrice?: number // 市场价
|
|
||||||
costPrice?: number // 成本价
|
|
||||||
barCode?: string // 商品条码
|
|
||||||
picUrl?: string // 图片地址
|
|
||||||
stock?: number // 库存
|
|
||||||
weight?: number // 商品重量,单位:kg 千克
|
|
||||||
volume?: number // 商品体积,单位:m^3 平米
|
|
||||||
subCommissionFirstPrice?: number // 一级分销的佣金
|
|
||||||
subCommissionSecondPrice?: number // 二级分销的佣金
|
|
||||||
salesCount?: number // 商品销量
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO puhui999:是不是直接叫 Spu 更简洁一点哈。type 待后面,总感觉有个类型?
|
|
||||||
export interface SpuType {
|
|
||||||
id?: number
|
|
||||||
name?: string // 商品名称
|
|
||||||
categoryId?: number | null // 商品分类
|
|
||||||
keyword?: string // 关键字
|
|
||||||
unit?: number | null // 单位
|
|
||||||
picUrl?: string // 商品封面图
|
|
||||||
sliderPicUrls?: string[] // 商品轮播图
|
|
||||||
introduction?: string // 商品简介
|
|
||||||
deliveryTemplateId?: number | null // 运费模版
|
|
||||||
brandId?: number | null // 商品品牌编号
|
|
||||||
specType?: boolean // 商品规格
|
|
||||||
subCommissionType?: boolean // 分销类型
|
|
||||||
skus: SkuType[] // sku数组
|
|
||||||
description?: string // 商品详情
|
|
||||||
sort?: string // 商品排序
|
|
||||||
giveIntegral?: number // 赠送积分
|
|
||||||
virtualSalesCount?: number // 虚拟销量
|
|
||||||
recommendHot?: boolean // 是否热卖
|
|
||||||
recommendBenefit?: boolean // 是否优惠
|
|
||||||
recommendBest?: boolean // 是否精品
|
|
||||||
recommendNew?: boolean // 是否新品
|
|
||||||
recommendGood?: boolean // 是否优品
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获得 Spu 列表
|
|
||||||
export const getSpuPage = (params: PageParam) => {
|
|
||||||
return request.get({ url: '/product/spu/page', params })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获得 Spu 列表 tabsCount
|
|
||||||
export const getTabsCount = () => {
|
|
||||||
return request.get({ url: '/product/spu/get-count' })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建商品 Spu
|
|
||||||
export const createSpu = (data: SpuType) => {
|
|
||||||
return request.post({ url: '/product/spu/create', data })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新商品 Spu
|
|
||||||
export const updateSpu = (data: SpuType) => {
|
|
||||||
return request.put({ url: '/product/spu/update', data })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新商品 Spu status
|
|
||||||
export const updateStatus = (data: { id: number; status: number }) => {
|
|
||||||
return request.put({ url: '/product/spu/update-status', data })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获得商品 Spu
|
|
||||||
export const getSpu = (id: number) => {
|
|
||||||
return request.get({ url: `/product/spu/get-detail?id=${id}` })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除商品 Spu
|
|
||||||
export const deleteSpu = (id: number) => {
|
|
||||||
return request.delete({ url: `/product/spu/delete?id=${id}` })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导出商品 Spu Excel
|
|
||||||
export const exportSpu = async (params) => {
|
|
||||||
return await request.download({ url: '/product/spu/export', params })
|
|
||||||
}
|
|
||||||
25
src/api/mall/purchase/index.js
Normal file
25
src/api/mall/purchase/index.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
// 查询列表
|
||||||
|
export const getPurchasePage = async (params) => {
|
||||||
|
return await request.get({ url: '/admin-api/crm/erp-purchase/page', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
export const createPurchase = async (data) => {
|
||||||
|
return await request.post({ url: '/admin-api/crm/erp-purchase/create', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改
|
||||||
|
export const updatePurchase = async (params) => {
|
||||||
|
return await request.put({ url: '/admin-api/crm/erp-purchase/update', data: params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
export const deletePurchase = async (id) => {
|
||||||
|
return await request.delete({ url: '/admin-api/crm/erp-purchase/delete?id=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审核
|
||||||
|
export const auditPurchase = async (params) => {
|
||||||
|
return await request.get({ url: '/admin-api/crm/erp-purchase/audit', params })
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@ export type DictDataVO = {
|
|||||||
|
|
||||||
// 查询字典数据(精简)列表
|
// 查询字典数据(精简)列表
|
||||||
export const listSimpleDictData = () => {
|
export const listSimpleDictData = () => {
|
||||||
return request.get({ url: '/admin-api/system/dict-data/simple-list' })
|
return request.get({ url: '/admin-api/crm/dict-data/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典数据列表
|
// 查询字典数据列表
|
||||||
|
|||||||
26
src/api/system/library/index.js
Normal file
26
src/api/system/library/index.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// 查询知识库列表
|
||||||
|
export const getLibraryPage = (params) => {
|
||||||
|
return request.get({ url: '/admin-api/crm/knowledge-lib/page', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询知识库详情
|
||||||
|
export const getLibrary = (id) => {
|
||||||
|
return request.get({ url: '/admin-api/crm/knowledge-lib/get?id=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增知识库
|
||||||
|
export const createLibrary = (data) => {
|
||||||
|
return request.post({ url: '/admin-api/crm/knowledge-lib/create', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改知识库
|
||||||
|
export const updateLibrary = (data) => {
|
||||||
|
return request.put({ url: '/admin-api/crm/knowledge-lib/update', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除知识库
|
||||||
|
export const deleteLibrary = (id) => {
|
||||||
|
return request.delete({ url: '/admin-api/crm/knowledge-lib/delete?id=' + id })
|
||||||
|
}
|
||||||
26
src/api/system/library/resource.js
Normal file
26
src/api/system/library/resource.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// 查询资源列表
|
||||||
|
export const getResourcePage = (params) => {
|
||||||
|
return request.get({ url: '/admin-api/crm/knowledge-lib-info/page', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询资源详情
|
||||||
|
export const getResource = (id) => {
|
||||||
|
return request.get({ url: '/admin-api/crm/knowledge-lib-info/get?id=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增资源
|
||||||
|
export const createResource = (data) => {
|
||||||
|
return request.post({ url: '/admin-api/crm/knowledge-lib-info/create', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改资源
|
||||||
|
export const updateResource = (data) => {
|
||||||
|
return request.put({ url: '/admin-api/crm/knowledge-lib-info/update', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除资源
|
||||||
|
export const deleteResource = (id) => {
|
||||||
|
return request.delete({ url: '/admin-api/crm/knowledge-lib-info/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 fieldName,后端接口参数名称,默认值wangeditor-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
|
||||||
|
|||||||
@@ -39,7 +39,7 @@
|
|||||||
<!-- 分页 -->
|
<!-- 分页 -->
|
||||||
<Pagination
|
<Pagination
|
||||||
v-model:limit="pageSize"
|
v-model:limit="pageSize"
|
||||||
v-model:page="currentPage"
|
v-model:page="pageNo"
|
||||||
:total="tableObject.total"
|
:total="tableObject.total"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
@@ -61,7 +61,7 @@ const emit = defineEmits(['update:tableObject', 'getList', 'getCheckedColumns'])
|
|||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { id: userId } = useUserStore().user //取用户ID
|
const { id: userId } = useUserStore().user //取用户ID
|
||||||
|
|
||||||
const currentPage = ref(props.tableObject?.currentPage || 1)
|
const pageNo = ref(props.tableObject?.pageNo || 1)
|
||||||
|
|
||||||
const pageSize = ref(props.tableObject?.pageSize || 20)
|
const pageSize = ref(props.tableObject?.pageSize || 20)
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ const checkedColumns = ref([])
|
|||||||
|
|
||||||
// 调用获取数据的接口,分页时需要使用
|
// 调用获取数据的接口,分页时需要使用
|
||||||
function getList({ page, limit }) {
|
function getList({ page, limit }) {
|
||||||
emit('update:tableObject', { ...props.tableObject, currentPage: page, pageSize: limit })
|
emit('update:tableObject', { ...props.tableObject, pageNo: page, pageSize: limit })
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
emit('getList')
|
emit('getList')
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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]>
|
||||||
|
|||||||
@@ -34,20 +34,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts" name="UploadFile">
|
<script setup lang="ts" name="UploadFile">
|
||||||
import { PropType } from 'vue'
|
|
||||||
|
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
import { getAccessToken, getTenantId, getAppId } from '@/utils/auth'
|
||||||
import type { UploadInstance, UploadUserFile, UploadProps, UploadRawFile } from 'element-plus'
|
import type { UploadInstance, UploadUserFile, UploadProps, UploadRawFile } from 'element-plus'
|
||||||
|
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: propTypes.oneOfType<string | string[]>([String, Array<String>]).isRequired,
|
||||||
type: Array as PropType<UploadUserFile[]>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
title: propTypes.string.def('文件上传'),
|
title: propTypes.string.def('文件上传'),
|
||||||
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
|
updateUrl: propTypes.string.def(import.meta.env.VITE_UPLOAD_URL),
|
||||||
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||||
@@ -66,8 +60,10 @@ const fileList = ref<UploadUserFile[]>(props.modelValue)
|
|||||||
const uploadNumber = ref<number>(0)
|
const uploadNumber = ref<number>(0)
|
||||||
const uploadHeaders = ref({
|
const uploadHeaders = ref({
|
||||||
Authorization: 'Bearer ' + getAccessToken(),
|
Authorization: 'Bearer ' + getAccessToken(),
|
||||||
'tenant-id': getTenantId()
|
'tenant-id': getTenantId(),
|
||||||
|
'instance-id': getAppId()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 文件上传之前判断
|
// 文件上传之前判断
|
||||||
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
const beforeUpload: UploadProps['beforeUpload'] = (file: UploadRawFile) => {
|
||||||
if (fileList.value.length >= props.limit) {
|
if (fileList.value.length >= props.limit) {
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ import type { UploadProps } from 'element-plus'
|
|||||||
|
|
||||||
import { generateUUID } from '@/utils'
|
import { generateUUID } from '@/utils'
|
||||||
import { propTypes } from '@/utils/propTypes'
|
import { propTypes } from '@/utils/propTypes'
|
||||||
import { getAccessToken, getTenantId } from '@/utils/auth'
|
import { getAccessToken, getTenantId, getAppId } from '@/utils/auth'
|
||||||
|
|
||||||
type FileTypes =
|
type FileTypes =
|
||||||
| 'image/apng'
|
| 'image/apng'
|
||||||
@@ -96,7 +96,8 @@ const deleteImg = () => {
|
|||||||
|
|
||||||
const uploadHeaders = ref({
|
const uploadHeaders = ref({
|
||||||
Authorization: 'Bearer ' + getAccessToken(),
|
Authorization: 'Bearer ' + getAccessToken(),
|
||||||
'tenant-id': getTenantId()
|
'tenant-id': getTenantId(),
|
||||||
|
'instance-id': getAppId()
|
||||||
})
|
})
|
||||||
|
|
||||||
const editImg = () => {
|
const editImg = () => {
|
||||||
|
|||||||
@@ -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 多图上传组件成功后只是把保存成功后的url替换掉组件选图时的文件路径,所以返回的fileList包含的是一个包含文件信息的对象列表
|
|
||||||
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;
|
||||||
|
|||||||
93
src/components/UploadFile/src/useUpload.ts
Normal file
93
src/components/UploadFile/src/useUpload.ts
Normal file
@@ -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'
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@ export function formatDate(date: Date, format?: string): string {
|
|||||||
}
|
}
|
||||||
// 日期存在,则进行格式化
|
// 日期存在,则进行格式化
|
||||||
if (format === undefined) {
|
if (format === undefined) {
|
||||||
format = 'YYYY-MM-DD HH:mm:ss'
|
format = 'YYYY-MM-DD'
|
||||||
}
|
}
|
||||||
return dayjs(date).format(format)
|
return dayjs(date).format(format)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog :title="title" v-model="dialogVisible" width="800px">
|
<Dialog :title="title" v-model="dialogVisible" width="800px">
|
||||||
<el-form :model="form" ref="addForm" :rules="rules" label-width="100px">
|
<el-form v-loading="formLoading" :model="form" ref="formRef" :rules="rules" label-width="100px">
|
||||||
<el-form-item label="知识库名称" prop="name">
|
<el-form-item label="知识库名称" prop="libName">
|
||||||
<el-input v-model="form.name" placeholder="请输入" />
|
<el-input v-model="form.libName" placeholder="请输入" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="资源类型" prop="type">
|
<el-form-item label="资源类型" prop="libType">
|
||||||
<el-radio-group v-model="form.type">
|
<el-radio-group v-model="form.libType">
|
||||||
<el-radio :label="1"> 文件 </el-radio>
|
<el-radio :label="1"> 文件 </el-radio>
|
||||||
<el-radio :label="2"> 纯图片 </el-radio>
|
<el-radio :label="2"> 纯图片 </el-radio>
|
||||||
<el-radio :label="3"> 自主编辑 </el-radio>
|
<el-radio :label="3"> 自主编辑 </el-radio>
|
||||||
@@ -18,49 +18,71 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span>
|
<span>
|
||||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
<el-button type="primary" @click="handleSave">保 存</el-button>
|
<el-button type="primary" :disabled="formLoading" @click="handleSave">保 存</el-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import * as LibraryApi from '@/api/system/library'
|
||||||
|
|
||||||
|
const emit = defineEmits(['success'])
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
|
||||||
const form = ref()
|
const form = ref()
|
||||||
const rules = ref({
|
const rules = ref({
|
||||||
name: { required: true, message: '名称不可为空', trigger: 'blur' }
|
libName: { required: true, message: '名称不可为空', trigger: 'blur' }
|
||||||
})
|
})
|
||||||
|
|
||||||
const title = ref('')
|
const title = ref('')
|
||||||
|
|
||||||
const addForm = ref()
|
const formRef = ref()
|
||||||
|
const formLoading = ref(false)
|
||||||
|
const formType = ref('create')
|
||||||
|
|
||||||
const emit = defineEmits(['update'])
|
const open = (type, val) => {
|
||||||
|
|
||||||
const open = (val) => {
|
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
formType.value = type
|
||||||
if (val) {
|
if (val) {
|
||||||
title.value = '修改知识库'
|
title.value = '修改知识库'
|
||||||
form.value = { ...val }
|
form.value = { ...val }
|
||||||
} else {
|
} else {
|
||||||
title.value = '新增知识库'
|
title.value = '新增知识库'
|
||||||
form.value = {
|
form.value = {
|
||||||
name: '',
|
libName: '',
|
||||||
type: 1,
|
libType: 1,
|
||||||
remark: undefined
|
remark: undefined,
|
||||||
|
isDefault: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
function handleSave() {
|
async function handleSave() {
|
||||||
addForm.value.validate((valid) => {
|
// 校验表单
|
||||||
if (valid) {
|
if (!formRef.value) return
|
||||||
emit('update', form.value)
|
const valid = await formRef.value.validate()
|
||||||
dialogVisible.value = false
|
if (!valid) return
|
||||||
|
// 提交请求
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
if (formType.value === 'create') {
|
||||||
|
await LibraryApi.createLibrary(form.value)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
} else {
|
||||||
|
await LibraryApi.updateLibrary(form.value)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
}
|
}
|
||||||
})
|
dialogVisible.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog :title="title" v-model="show" width="800px">
|
<Dialog :title="title" v-model="show" width="800px">
|
||||||
<el-form :model="form" ref="resourceForm" :rules="rules" label-width="60px">
|
<el-form v-loading="formLoading" :model="form" ref="formRef" :rules="rules" label-width="60px">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12" :offset="0">
|
<el-col :span="12" :offset="0">
|
||||||
<el-form-item label="标题" prop="title">
|
<el-form-item label="标题" prop="title">
|
||||||
@@ -8,16 +8,19 @@
|
|||||||
</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="tipList">
|
<el-form-item label="标签" prop="tags">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.tipList"
|
v-model="form.tags"
|
||||||
multiple
|
multiple
|
||||||
filterable
|
filterable
|
||||||
|
collapse-tags
|
||||||
|
collapse-tags-tooltip
|
||||||
allow-create
|
allow-create
|
||||||
default-first-option
|
default-first-option
|
||||||
:reserve-keyword="false"
|
:reserve-keyword="false"
|
||||||
placeholder="请选择标签或输入"
|
placeholder="请选择标签或输入"
|
||||||
clearable
|
clearable
|
||||||
|
@change="tipChange"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
@@ -33,15 +36,10 @@
|
|||||||
<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="content">
|
<el-form-item label="内容" prop="content">
|
||||||
<UploadFile
|
<UploadFile v-if="libType == 1" v-model="form.files" :limit="1" :isShowTip="false" />
|
||||||
v-if="form.type == 1"
|
<UploadImg
|
||||||
v-model="form.sliderPicUrls"
|
v-else-if="libType == 2"
|
||||||
:isShowTip="false"
|
v-model:modelValue="form.files"
|
||||||
:fileType="[]"
|
|
||||||
/>
|
|
||||||
<UploadImgs
|
|
||||||
v-else-if="form.type == 2"
|
|
||||||
v-model:modelValue="form.sliderPicUrls"
|
|
||||||
width="100px"
|
width="100px"
|
||||||
height="100px"
|
height="100px"
|
||||||
/>
|
/>
|
||||||
@@ -60,41 +58,107 @@
|
|||||||
<template #footer>
|
<template #footer>
|
||||||
<span>
|
<span>
|
||||||
<el-button @click="show = false">取 消</el-button>
|
<el-button @click="show = false">取 消</el-button>
|
||||||
<el-button type="primary" @click="handleSave">保 存</el-button>
|
<el-button type="primary" :disabled="formLoading" @click="handleSave">保 存</el-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup name="DialogResource">
|
||||||
|
import * as ResourceApi from '@/api/system/library/resource'
|
||||||
|
import { getDictOptions } from '@/utils/dict'
|
||||||
|
import * as dictApi from '@/api/system/dict/dict.data'
|
||||||
|
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
const show = ref(false)
|
const show = ref(false)
|
||||||
const title = ref('')
|
const title = ref('')
|
||||||
|
const formType = ref('create')
|
||||||
|
const libType = ref(1)
|
||||||
|
|
||||||
const form = ref({})
|
const form = ref({})
|
||||||
const rules = ref({})
|
const formLoading = ref(false)
|
||||||
|
const rules = ref({
|
||||||
|
title: { required: true, message: '标题不可为空', trigger: 'blur' }
|
||||||
|
})
|
||||||
|
|
||||||
const tipOptions = ref([{ label: '绿色', value: '绿色' }])
|
const tipOptions = ref([])
|
||||||
|
|
||||||
function open(type, val) {
|
function getOptions() {
|
||||||
|
tipOptions.value = getDictOptions('knowledge_tags')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function open(type, info, id) {
|
||||||
show.value = true
|
show.value = true
|
||||||
if (val) {
|
title.value = type == 'update' ? '修改资源' : '新增资源'
|
||||||
title.value = '修改资源'
|
formType.value = type
|
||||||
form.value = { ...val, type }
|
libType.value = info.libType
|
||||||
} else {
|
resetForm(info.libId)
|
||||||
title.value = '新增资源'
|
getOptions()
|
||||||
form.value = {
|
if (id) {
|
||||||
type,
|
formLoading.value = true
|
||||||
title: '',
|
try {
|
||||||
tipList: [],
|
form.value = await ResourceApi.getResource(id)
|
||||||
sliderPicUrls: [],
|
} finally {
|
||||||
content: '',
|
formLoading.value = false
|
||||||
remark: null
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetForm(libId) {
|
||||||
|
form.value = {
|
||||||
|
libId,
|
||||||
|
title: '',
|
||||||
|
tags: [],
|
||||||
|
files: '',
|
||||||
|
content: '',
|
||||||
|
remark: null,
|
||||||
|
isDefault: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tipChange(val) {
|
||||||
|
const valStr = val.at(-1)
|
||||||
|
if (!tipOptions.value.some((it) => it.value == valStr)) {
|
||||||
|
await dictApi.createDictData({
|
||||||
|
label: valStr,
|
||||||
|
value: valStr,
|
||||||
|
sort: 1,
|
||||||
|
status: 0,
|
||||||
|
dictType: 'knowledge_tags'
|
||||||
|
})
|
||||||
|
tipOptions.value.push({ label: valStr, value: valStr })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
function handleSave() {
|
const formRef = ref()
|
||||||
console.log('保存成功')
|
|
||||||
|
/** 提交表单 */
|
||||||
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||||
|
async function handleSave() {
|
||||||
|
// 校验表单
|
||||||
|
if (!formRef.value) return
|
||||||
|
const valid = await formRef.value.validate()
|
||||||
|
if (!valid) return
|
||||||
|
// 提交请求
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
if (formType.value === 'create') {
|
||||||
|
await ResourceApi.createResource(form.value)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
} else {
|
||||||
|
await ResourceApi.updateResource(form.value)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
|
}
|
||||||
|
show.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<teleport v-if="show" to="#app">
|
<teleport v-if="show" to="#app">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- <Icon class="ep:circle-close close-icon" /> -->
|
|
||||||
<el-icon class="close-icon" @click="show = false"><CircleClose /></el-icon>
|
<el-icon class="close-icon" @click="show = false"><CircleClose /></el-icon>
|
||||||
<el-drawer
|
<el-drawer
|
||||||
v-model="showDrawer"
|
v-model="showDrawer"
|
||||||
@@ -16,22 +15,30 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<span style="color: #fff">资源详情</span>
|
<span style="color: #fff">资源详情</span>
|
||||||
</template>
|
</template>
|
||||||
<el-form style="flex: 1" :model="info" label-width="80px" label-position="left">
|
<div>
|
||||||
<el-form-item label="标题:">
|
<div class="flex mb-18px">
|
||||||
{{ info.title }}
|
<div class="w-80px text-light-50">标题:</div>
|
||||||
</el-form-item>
|
<div class="flex-1 ml-10px text-light-50">{{ info.title }}</div>
|
||||||
<el-form-item label="文件名称:">
|
</div>
|
||||||
{{ info.fileName }}
|
<div class="flex mb-18px">
|
||||||
</el-form-item>
|
<div class="w-80px text-light-50">文件名称:</div>
|
||||||
<el-form-item label="标签:">
|
<div class="flex-1 ml-10px text-light-50 w-80px" style="word-wrap: break-word">{{
|
||||||
<el-tag class="mr-5px mb-5px" v-for="(item, index) in info.tipList" :key="index">{{
|
info.files
|
||||||
item
|
}}</div>
|
||||||
}}</el-tag>
|
</div>
|
||||||
</el-form-item>
|
<div class="flex mb-18px">
|
||||||
<el-form-item label="备注:">
|
<div class="w-80px text-light-50">标签:</div>
|
||||||
{{ info.remark }}
|
<div class="flex-1 ml-10px w-80px">
|
||||||
</el-form-item>
|
<el-tag class="mr-5px" v-for="(item, index) in info.tags" :key="index">
|
||||||
</el-form>
|
{{ getDictLabel('knowledge_tags', item) }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex mb-18px">
|
||||||
|
<div class="w-80px text-light-50">备注:</div>
|
||||||
|
<div class="flex-1 ml-10px text-light-50">{{ info.remark }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<el-button plain :disabled="imgIndex <= 0" @click="imgIndex--">上一张</el-button>
|
<el-button plain :disabled="imgIndex <= 0" @click="imgIndex--">上一张</el-button>
|
||||||
@@ -42,13 +49,14 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
|
||||||
<img :src="info.fileUrl" :alt="info.fileName" srcset="" class="width-fit img" />
|
<img :src="info.files" :alt="info.files" srcset="" class="width-fit img" />
|
||||||
<div class="img-idx">{{ imgIndex + 1 }} / {{ imgList.length }}</div>
|
<div class="img-idx">{{ imgIndex + 1 }} / {{ imgList.length }}</div>
|
||||||
</div>
|
</div>
|
||||||
</teleport>
|
</teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { getDictLabel } from '@/utils/dict'
|
||||||
import { ElIcon } from 'element-plus'
|
import { ElIcon } from 'element-plus'
|
||||||
import { CircleClose } from '@element-plus/icons-vue'
|
import { CircleClose } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,9 @@
|
|||||||
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
<el-card shadow="always" :body-style="{ padding: '10px' }">
|
||||||
<div class="flex justify-between items-center" style="width: 400px">
|
<div class="flex justify-between items-center" style="width: 400px">
|
||||||
<div class="text-16px font-bold">知识库名称</div>
|
<div class="text-16px font-bold">知识库名称</div>
|
||||||
<el-button type="primary" style="padding: 0px" text @click="handleAdd">新增</el-button>
|
<el-button type="primary" style="padding: 0px" text @click="openForm('create', null)"
|
||||||
|
>新增</el-button
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="border-top-1px mt-10px pt-10px">
|
<div class="border-top-1px mt-10px pt-10px">
|
||||||
<div
|
<div
|
||||||
@@ -11,24 +13,27 @@
|
|||||||
v-for="(item, index) in libraryList"
|
v-for="(item, index) in libraryList"
|
||||||
:key="index"
|
:key="index"
|
||||||
:class="{ actived: libraryIndex == index }"
|
:class="{ actived: libraryIndex == index }"
|
||||||
@click="libraryIndex = index"
|
@click="handleClickLib(index)"
|
||||||
>
|
>
|
||||||
<div class="flex-1 text-14px">{{ item.name }}</div>
|
<div class="flex-1 text-14px">{{ item.libName }}</div>
|
||||||
<div class="ml-10px">
|
<div class="ml-10px">
|
||||||
<el-button type="primary" style="padding: 0px" text @click="handleUpdate(item)"
|
<el-button type="primary" style="padding: 0px" text @click="openForm('update', item)">
|
||||||
>修改</el-button
|
修改
|
||||||
>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
class="ml-10px"
|
class="ml-10px"
|
||||||
style="padding: 0px"
|
style="padding: 0px"
|
||||||
text
|
text
|
||||||
@click="handleRemove(index)"
|
@click="handleRemove(item.libId)"
|
||||||
>删除</el-button
|
|
||||||
>
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Pagination
|
<Pagination
|
||||||
|
small
|
||||||
|
layout="total, prev, pager, next, jumper"
|
||||||
v-model:limit="pageSize"
|
v-model:limit="pageSize"
|
||||||
v-model:page="currentPage"
|
v-model:page="currentPage"
|
||||||
:total="total"
|
:total="total"
|
||||||
@@ -36,44 +41,55 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card class="ml-20px" style="flex: 1" shadow="always" :body-style="{ padding: '10px' }">
|
<el-card
|
||||||
|
v-if="libraryList.length"
|
||||||
|
class="ml-20px"
|
||||||
|
style="flex: 1"
|
||||||
|
shadow="always"
|
||||||
|
:body-style="{ padding: '10px' }"
|
||||||
|
>
|
||||||
<div class="flex justify-between items-center border-bottom-1px pb-10px mb-20px">
|
<div class="flex justify-between items-center border-bottom-1px pb-10px mb-20px">
|
||||||
<div>【{{ libraryList[libraryIndex].name }}】资源详情</div>
|
<div>【{{ libraryList[libraryIndex].libName }}】资源详情</div>
|
||||||
<el-button type="primary" @click="handleAddResource">新增资源</el-button>
|
<el-button type="primary" @click="openResource('create', null)">新增资源</el-button>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="libraryList[libraryIndex].type == 1">
|
<div v-if="libraryList[libraryIndex].libType == 1">
|
||||||
<el-table :data="tableList" border>
|
<el-table :data="tableList" border v-loading="loading">
|
||||||
<el-table-column type="index" width="50" />
|
<el-table-column type="index" width="50" />
|
||||||
<el-table-column prop="title" label="标题" width="200" />
|
<el-table-column prop="title" label="标题" width="200" />
|
||||||
<el-table-column label="标签" width="200px">
|
<el-table-column label="标签" width="200px">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag v-for="(item, index) in row.tipList" :key="index" class="mr-5px">{{
|
<el-tag v-for="(item, index) in row.tags" :key="index" class="mr-5px">
|
||||||
item
|
{{ getDictLabel('knowledge_tags', item) }}
|
||||||
}}</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="fileName" label="附件">
|
<el-table-column prop="files" label="附件">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-link type="primary" underline :href="row.fileUrl" target="_blank">{{
|
<el-link type="primary" underline :href="row.fileUrl" target="_blank">
|
||||||
row.fileName
|
{{ row.files }}
|
||||||
}}</el-link>
|
</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="120px">
|
<el-table-column label="操作" width="120px">
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<el-button type="primary" style="padding: 0" text @click="updateResource(row)"
|
<el-button
|
||||||
>修改</el-button
|
type="primary"
|
||||||
>
|
style="padding: 0"
|
||||||
<el-button type="danger" style="padding: 0" text @click="removeResource($index)"
|
text
|
||||||
>删除</el-button
|
@click="openResource('update', row.id)"
|
||||||
>
|
>
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button type="danger" style="padding: 0" text @click="removeResource($index)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="libraryList[libraryIndex].type == 2" class="flex">
|
<div v-else-if="libraryList[libraryIndex].libType == 2" class="flex">
|
||||||
<div v-for="(item, index) in tableList" :key="index" class="mr-10px">
|
<div v-for="(item, index) in tableList" :key="index" class="mr-10px">
|
||||||
<el-image :src="item.fileUrl" @click="imagePreview(index)" class="w-150px h-150px" />
|
<el-image :src="item.files" @click="imagePreview(index)" class="w-150px h-150px" />
|
||||||
<div class="mt-5px text-center">{{ item.title }}</div>
|
<div class="mt-5px text-center">{{ item.title }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,19 +99,24 @@
|
|||||||
<el-table-column prop="title" label="标题" />
|
<el-table-column prop="title" label="标题" />
|
||||||
<el-table-column label="标签">
|
<el-table-column label="标签">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag v-for="(item, index) in row.tipList" :key="index" class="mr-5px">{{
|
<el-tag v-for="(item, index) in row.tags" :key="index" class="mr-5px">
|
||||||
item
|
{{ getDictLabel('knowledge_tags', item) }}
|
||||||
}}</el-tag>
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="120px">
|
<el-table-column label="操作" width="120px">
|
||||||
<template #default="{ row, $index }">
|
<template #default="{ row, $index }">
|
||||||
<el-button type="primary" style="padding: 0" text @click="updateResource(row)"
|
<el-button
|
||||||
>修改</el-button
|
type="primary"
|
||||||
>
|
style="padding: 0"
|
||||||
<el-button type="danger" style="padding: 0" text @click="removeResource($index)"
|
text
|
||||||
>删除</el-button
|
@click="openResource('update', row.infoId)"
|
||||||
>
|
>
|
||||||
|
修改
|
||||||
|
</el-button>
|
||||||
|
<el-button type="danger" style="padding: 0" text @click="removeResource($index)">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -108,27 +129,31 @@
|
|||||||
/>
|
/>
|
||||||
</el-card>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
<DialogLibrary ref="library" @update="afterSaveLibrary" />
|
<DialogLibrary ref="library" @success="getList" />
|
||||||
<DialogResource ref="resourceDialog" />
|
<DialogResource ref="resourceDialog" @success="getResourceList" />
|
||||||
<ImagePreview ref="imgPreview" />
|
<ImagePreview ref="imgPreview" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup name="Library">
|
||||||
|
import * as LibraryApi from '@/api/system/library/index'
|
||||||
|
import * as ResourceApi from '@/api/system/library/resource'
|
||||||
import DialogLibrary from './Comp/DialogLibrary.vue'
|
import DialogLibrary from './Comp/DialogLibrary.vue'
|
||||||
import DialogResource from './Comp/DialogResource.vue'
|
import DialogResource from './Comp/DialogResource.vue'
|
||||||
import ImagePreview from './Comp/ImagePreview.vue'
|
import ImagePreview from './Comp/ImagePreview.vue'
|
||||||
|
import { getDictLabel } from '@/utils/dict'
|
||||||
|
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
const libraryIndex = ref(0)
|
const libraryIndex = ref(0)
|
||||||
const libraryList = ref([
|
const libraryList = ref([])
|
||||||
{ name: '成交案例', id: 1, type: 2 },
|
|
||||||
{ name: '公司资质材料', id: 2, type: 1 },
|
|
||||||
{ name: '会议纪要', id: 3, type: 3 }
|
|
||||||
])
|
|
||||||
|
|
||||||
const pageSize = ref(20)
|
const pageSize = ref(20)
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
const library = ref()
|
const library = ref()
|
||||||
const resourceDialog = ref()
|
const resourceDialog = ref()
|
||||||
const imgPreview = ref()
|
const imgPreview = ref()
|
||||||
@@ -137,61 +162,70 @@ const resourcePageSize = ref(20)
|
|||||||
const resourcePageNum = ref(1)
|
const resourcePageNum = ref(1)
|
||||||
const resourceTotal = ref(0)
|
const resourceTotal = ref(0)
|
||||||
|
|
||||||
const tableList = ref([
|
const tableList = ref([])
|
||||||
{
|
|
||||||
fileUrl:
|
|
||||||
'https://img0.baidu.com/it/u=1033018635,7901815&fm=253&fmt=auto&app=138&f=JPEG?w=750&h=500',
|
|
||||||
fileName: '测试图片1'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tipList: ['优质材料', '无污染'],
|
|
||||||
fileUrl:
|
|
||||||
'https://img0.baidu.com/it/u=1610680713,975251961&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750',
|
|
||||||
fileName: '测试图片2'
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
function handleAdd() {
|
function openForm(type, item) {
|
||||||
// 新增知识库
|
library.value.open(type, item)
|
||||||
library.value.open(null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleUpdate(item) {
|
async function handleRemove(id) {
|
||||||
library.value.open(item)
|
try {
|
||||||
|
// 删除的二次确认
|
||||||
|
await message.delConfirm()
|
||||||
|
// 发起删除
|
||||||
|
await LibraryApi.deleteLibrary(id)
|
||||||
|
message.success(t('common.delSuccess'))
|
||||||
|
// 刷新列表
|
||||||
|
await getList()
|
||||||
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleRemove(index) {
|
async function getList() {
|
||||||
libraryList.value.splice(index, 1)
|
const data = await LibraryApi.getLibraryPage({
|
||||||
|
pageNo: currentPage.value,
|
||||||
|
pageSize: pageSize.value
|
||||||
|
})
|
||||||
|
libraryList.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
handleClickLib(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function afterSaveLibrary(val) {
|
function handleClickLib(index) {
|
||||||
libraryList.value.push(val)
|
libraryIndex.value = index
|
||||||
|
getResourceList()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getList() {
|
function openResource(type, id) {
|
||||||
libraryList.value = []
|
resourceDialog.value.open(type, libraryList.value[libraryIndex.value], id)
|
||||||
}
|
|
||||||
|
|
||||||
function handleAddResource() {
|
|
||||||
resourceDialog.value.open(libraryList.value[libraryIndex.value].type, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateResource(row) {
|
|
||||||
resourceDialog.value.open(libraryList.value[libraryIndex.value].type, row)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeResource(index) {
|
function removeResource(index) {
|
||||||
tableList.value.splice(index, 1)
|
tableList.value.splice(index, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getResourceList() {
|
async function getResourceList() {
|
||||||
tableList.value = []
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await ResourceApi.getResourcePage({
|
||||||
|
libId: libraryList.value[libraryIndex.value].libId,
|
||||||
|
pageNo: resourcePageNum.value,
|
||||||
|
pageSize: resourcePageSize.value
|
||||||
|
})
|
||||||
|
tableList.value = data.list
|
||||||
|
resourceTotal.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 商品图预览 */
|
/** 商品图预览 */
|
||||||
function imagePreview(index) {
|
function imagePreview(index) {
|
||||||
imgPreview.value.open(index, tableList.value)
|
imgPreview.value.open(index, tableList.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -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
|
<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-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
|
||||||
|
type="primary"
|
||||||
|
class="mr-10px"
|
||||||
|
link
|
||||||
|
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() {
|
||||||
|
|||||||
50
src/views/Clue/Set/Comp/GeneralSet.vue
Normal file
50
src/views/Clue/Set/Comp/GeneralSet.vue
Normal file
@@ -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,33 @@
|
|||||||
<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.brandId)"
|
||||||
<el-button type="danger" text @click="handleDelete(scope.row)">删除</el-button>
|
>修改</el-button
|
||||||
|
>
|
||||||
|
<el-button type="danger" text @click="handleDelete(scope.row.brandId)">删除</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,56 +51,70 @@
|
|||||||
</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 message = useMessage() // 消息弹窗
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
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) {
|
}
|
||||||
brandDialog.value.open(type, info)
|
|
||||||
}
|
function openForm(type, id) {
|
||||||
|
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()
|
||||||
} catch {}
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@@ -15,30 +15,34 @@
|
|||||||
<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"
|
<el-table-column prop="name" label="分类名称" />
|
||||||
:data="tableList"
|
<el-table-column prop="sort" label="排序" width="100px" />
|
||||||
row-key="categoryId"
|
|
||||||
:tree-props="{ children: 'children' }"
|
|
||||||
>
|
|
||||||
<el-table-column prop="categoryName" label="分类名称" />
|
|
||||||
<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="状态" 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>
|
||||||
<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"
|
||||||
/>
|
/>
|
||||||
@@ -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) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
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"
|
||||||
@@ -7,14 +7,20 @@
|
|||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="80px"
|
label-width="80px"
|
||||||
>
|
>
|
||||||
<el-form-item v-if="formData.level > 1" label="上级分类">
|
<el-form-item v-if="formData.parentCategory" 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) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
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,19 +65,20 @@ 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.parentCategory = info.name
|
||||||
formData.value.parentCategory = info.categoryName
|
formData.value.parentId = info.id
|
||||||
}
|
}
|
||||||
// formData.value = await UserApi.getUser(id)
|
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
formData.value.parentId = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
@@ -83,13 +93,12 @@ const submitForm = async () => {
|
|||||||
// 提交请求
|
// 提交请求
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
// const data = formData.value as unknown as UserApi.UserVO
|
if (formType.value === 'update') {
|
||||||
if (formType.value === 'create') {
|
await ProductCategoryApi.updateCategory(formData.value)
|
||||||
// await UserApi.createUser(data)
|
|
||||||
message.success(t('common.createSuccess'))
|
|
||||||
} else {
|
|
||||||
// await UserApi.updateUser(data)
|
|
||||||
message.success(t('common.updateSuccess'))
|
message.success(t('common.updateSuccess'))
|
||||||
|
} else {
|
||||||
|
await ProductCategoryApi.createCategory(formData.value)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
}
|
}
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
// 发送操作成功的事件
|
// 发送操作成功的事件
|
||||||
@@ -102,8 +111,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()
|
||||||
|
|||||||
@@ -7,11 +7,11 @@
|
|||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="80px"
|
label-width="80px"
|
||||||
>
|
>
|
||||||
<el-form-item label="名称" prop="name">
|
<el-form-item label="名称" prop="label">
|
||||||
<el-input v-model="formData.name" placeholder="请输入供应商名称" />
|
<el-input v-model="formData.label" 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>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input
|
<el-input
|
||||||
@@ -29,6 +29,7 @@
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
<script name="DialogSupplier" setup>
|
<script name="DialogSupplier" setup>
|
||||||
|
import * as dictApi from '@/api/system/dict/dict.data'
|
||||||
const { t } = useI18n() // 国际化
|
const { t } = useI18n() // 国际化
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
@@ -37,27 +38,26 @@ const dialogTitle = ref('') // 弹窗的标题
|
|||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
name: '',
|
label: '',
|
||||||
orderNum: 1,
|
sort: 1,
|
||||||
remark: ''
|
remark: ''
|
||||||
})
|
})
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
|
label: [{ 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.id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
formData.value = { ...info }
|
formData.value = await dictApi.getDictData(id)
|
||||||
// formData.value = await UserApi.getUser(id)
|
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
@@ -75,12 +75,14 @@ const submitForm = async () => {
|
|||||||
// 提交请求
|
// 提交请求
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
// const data = formData.value as unknown as UserApi.UserVO
|
if (!formData.value.value) {
|
||||||
|
formData.value.value = formData.value.label
|
||||||
|
}
|
||||||
if (formType.value === 'create') {
|
if (formType.value === 'create') {
|
||||||
// await UserApi.createUser(data)
|
await dictApi.createDictData(formData.value)
|
||||||
message.success(t('common.createSuccess'))
|
message.success(t('common.createSuccess'))
|
||||||
} else {
|
} else {
|
||||||
// await UserApi.updateUser(data)
|
await dictApi.updateDictData(formData.value)
|
||||||
message.success(t('common.updateSuccess'))
|
message.success(t('common.updateSuccess'))
|
||||||
}
|
}
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
@@ -94,8 +96,10 @@ const submitForm = async () => {
|
|||||||
/** 重置表单 */
|
/** 重置表单 */
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
name: '',
|
label: '',
|
||||||
orderNum: 1,
|
sort: 1,
|
||||||
|
status: 0,
|
||||||
|
dictType: 'erp_supplier',
|
||||||
remark: ''
|
remark: ''
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
|
|||||||
@@ -16,23 +16,27 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<el-table v-loading="loading" :data="tableList">
|
<el-table v-loading="loading" :data="tableList">
|
||||||
<el-table-column prop="name" label="供应商名称" />
|
<el-table-column prop="label" 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="remark" label="备注" />
|
||||||
<el-table-column label="创建时间" prop="createTime" width="180px" />
|
<el-table-column
|
||||||
<el-table-column label="创建人" prop="createUser" width="150px" />
|
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="getList"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<DialogSupplier ref="brandDialog" @success="handleQuery" />
|
<DialogSupplier ref="brandDialog" @success="handleQuery" />
|
||||||
@@ -40,11 +44,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="SupplierSet">
|
<script setup name="SupplierSet">
|
||||||
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import DialogSupplier from './DialogSupplier.vue'
|
import DialogSupplier from './DialogSupplier.vue'
|
||||||
|
import * as dictApi from '@/api/system/dict/dict.data'
|
||||||
|
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
|
||||||
const searchForm = ref({
|
const searchForm = ref({
|
||||||
pageNum: 1,
|
label: '',
|
||||||
pageSize: 20
|
pageSize: 20,
|
||||||
|
pageNo: 1,
|
||||||
|
dictType: 'erp_supplier'
|
||||||
})
|
})
|
||||||
|
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
@@ -53,43 +64,49 @@ const tableList = 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: '',
|
label: '',
|
||||||
pageSize: 20,
|
pageSize: 20,
|
||||||
pageNum: 1
|
pageNo: 1,
|
||||||
|
dictType: 'erp_supplier'
|
||||||
}
|
}
|
||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getList() {
|
async function getList() {
|
||||||
tableList.value = [
|
loading.value = true
|
||||||
{
|
try {
|
||||||
id: 1,
|
const data = await dictApi.getDictDataPage(searchForm.value)
|
||||||
name: '测试'
|
tableList.value = data.list
|
||||||
}
|
total.value = data.total
|
||||||
]
|
} finally {
|
||||||
}
|
loading.value = false
|
||||||
|
}
|
||||||
function openForm(type, info) {
|
}
|
||||||
brandDialog.value.open(type, info)
|
|
||||||
}
|
function openForm(type, id) {
|
||||||
|
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 dictApi.deleteDictData(id)
|
||||||
message.success(t('common.delSuccess'))
|
message.success(t('common.delSuccess'))
|
||||||
// 刷新列表
|
// 刷新列表
|
||||||
await getList()
|
await getList()
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
handleQuery()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
:model="formData"
|
:model="formData"
|
||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="80px"
|
label-width="80px"
|
||||||
|
@submit.prevent
|
||||||
>
|
>
|
||||||
<el-form-item label="属性名称" prop="name">
|
<el-form-item label="属性名称" prop="name">
|
||||||
<el-input v-model="formData.name" placeholder="请输入名称" />
|
<el-input v-model="formData.name" placeholder="请输入名称" />
|
||||||
@@ -33,7 +34,7 @@ const formRules = reactive({
|
|||||||
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
|
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
|
||||||
})
|
})
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
const attributeList = ref([]) // 商品属性列表
|
const attributeList = ref<any>([]) // 商品属性列表
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
propertyList: {
|
propertyList: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@@ -69,17 +70,13 @@ const submitForm = async () => {
|
|||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = formData.value as PropertyApi.PropertyVO
|
const data = formData.value as PropertyApi.PropertyVO
|
||||||
// 检查属性是否已存在,如果有则返回属性和其下属性值
|
const propertyId = await PropertyApi.createProperty(data)
|
||||||
const res = await PropertyApi.getPropertyListAndValue({ name: data.name })
|
// 添加到属性列表
|
||||||
if (res.length === 0) {
|
attributeList.value.push({
|
||||||
const propertyId = await PropertyApi.createProperty(data)
|
id: propertyId + '',
|
||||||
attributeList.value.push({ id: propertyId, ...formData.value, values: [] })
|
...formData.value,
|
||||||
} else {
|
values: []
|
||||||
if (res[0].values === null) {
|
})
|
||||||
res[0].values = []
|
|
||||||
}
|
|
||||||
attributeList.value.push(res[0]) // 因为只用一个
|
|
||||||
}
|
|
||||||
message.success(t('common.createSuccess'))
|
message.success(t('common.createSuccess'))
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
} finally {
|
} finally {
|
||||||
@@ -90,8 +87,7 @@ const submitForm = async () => {
|
|||||||
/** 重置表单 */
|
/** 重置表单 */
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
name: '',
|
name: ''
|
||||||
remark: ''
|
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,317 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-table
|
|
||||||
:data="isBatch ? skuList : formData.skus"
|
|
||||||
border
|
|
||||||
class="tabNumWidth"
|
|
||||||
max-height="500"
|
|
||||||
size="small"
|
|
||||||
>
|
|
||||||
<el-table-column align="center" fixed="left" label="图片" min-width="100">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<UploadImg v-model="row.picUrl" height="80px" width="100%" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-if="formData.specType && !isBatch">
|
|
||||||
<!-- 根据商品属性动态添加 -->
|
|
||||||
<el-table-column
|
|
||||||
v-for="(item, index) in tableHeaders"
|
|
||||||
:key="index"
|
|
||||||
:label="item.label"
|
|
||||||
align="center"
|
|
||||||
min-width="120"
|
|
||||||
>
|
|
||||||
<template #default="{ row }">
|
|
||||||
<!-- TODO puhui999:展示成蓝色,有点区分度哈 -->
|
|
||||||
{{ row.properties[index]?.valueName }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</template>
|
|
||||||
<el-table-column align="center" label="商品条码" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input v-model="row.barCode" class="w-100%" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="销售价(元)" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number v-model="row.price" :min="0" :precision="2" :step="0.1" class="w-100%" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="市场价(元)" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.marketPrice"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:step="0.1"
|
|
||||||
class="w-100%"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="成本价(元)" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.costPrice"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:step="0.1"
|
|
||||||
class="w-100%"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="库存" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number v-model="row.stock" :min="0" class="w-100%" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="重量(kg)" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number v-model="row.weight" :min="0" :precision="2" :step="0.1" class="w-100%" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="体积(m^3)" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number v-model="row.volume" :min="0" :precision="2" :step="0.1" class="w-100%" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<template v-if="formData.subCommissionType">
|
|
||||||
<el-table-column align="center" label="一级返佣(元)" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.subCommissionFirstPrice"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:step="0.1"
|
|
||||||
class="w-100%"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column align="center" label="二级返佣(元)" min-width="168">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-input-number
|
|
||||||
v-model="row.subCommissionSecondPrice"
|
|
||||||
:min="0"
|
|
||||||
:precision="2"
|
|
||||||
:step="0.1"
|
|
||||||
class="w-100%"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</template>
|
|
||||||
<el-table-column v-if="formData.specType" align="center" fixed="right" label="操作" width="80">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-button v-if="isBatch" link size="small" type="primary" @click="batchAdd">
|
|
||||||
批量添加
|
|
||||||
</el-button>
|
|
||||||
<el-button v-else link size="small" type="primary" @click="deleteSku(row)">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
</template>
|
|
||||||
<script lang="ts" name="SkuList" setup>
|
|
||||||
import { PropType } from 'vue'
|
|
||||||
import { copyValueToTarget } from '@/utils'
|
|
||||||
import { propTypes } from '@/utils/propTypes'
|
|
||||||
import { UploadImg } from '@/components/UploadFile'
|
|
||||||
import type { Property, SkuType, SpuType } from '@/api/mall/product/spu'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
propFormData: {
|
|
||||||
type: Object as PropType<SpuType>,
|
|
||||||
default: () => {}
|
|
||||||
},
|
|
||||||
propertyList: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
isBatch: propTypes.bool.def(false) // 是否作为批量操作组件
|
|
||||||
})
|
|
||||||
const formData = ref<SpuType>() // 表单数据
|
|
||||||
const skuList = ref<SkuType[]>([
|
|
||||||
{
|
|
||||||
price: 0, // 商品价格
|
|
||||||
marketPrice: 0, // 市场价
|
|
||||||
costPrice: 0, // 成本价
|
|
||||||
barCode: '', // 商品条码
|
|
||||||
picUrl: '', // 图片地址
|
|
||||||
stock: 0, // 库存
|
|
||||||
weight: 0, // 商品重量
|
|
||||||
volume: 0, // 商品体积
|
|
||||||
subCommissionFirstPrice: 0, // 一级分销的佣金
|
|
||||||
subCommissionSecondPrice: 0 // 二级分销的佣金
|
|
||||||
}
|
|
||||||
]) // 批量添加时的临时数据
|
|
||||||
// TODO @puhui999:保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种。
|
|
||||||
|
|
||||||
/** 批量添加 */
|
|
||||||
const batchAdd = () => {
|
|
||||||
formData.value.skus.forEach((item) => {
|
|
||||||
copyValueToTarget(item, skuList.value[0])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除 sku */
|
|
||||||
const deleteSku = (row) => {
|
|
||||||
const index = formData.value.skus.findIndex(
|
|
||||||
// 直接把列表转成字符串比较
|
|
||||||
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
|
|
||||||
)
|
|
||||||
formData.value.skus.splice(index, 1)
|
|
||||||
}
|
|
||||||
const tableHeaders = ref<{ prop: string; label: string }[]>([]) // 多属性表头
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将传进来的值赋值给 skuList
|
|
||||||
*/
|
|
||||||
watch(
|
|
||||||
() => props.propFormData,
|
|
||||||
(data) => {
|
|
||||||
if (!data) return
|
|
||||||
formData.value = data
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/** 生成表数据 */
|
|
||||||
const generateTableData = (propertyList: any[]) => {
|
|
||||||
// 构建数据结构
|
|
||||||
const propertyValues = propertyList.map((item) =>
|
|
||||||
item.values.map((v) => ({
|
|
||||||
propertyId: item.id,
|
|
||||||
propertyName: item.name,
|
|
||||||
valueId: v.id,
|
|
||||||
valueName: v.name
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
// TODO @puhui:是不是 buildSkuList,这样容易理解一点哈。item 改成 sku
|
|
||||||
const buildList = build(propertyValues)
|
|
||||||
// 如果回显的 sku 属性和添加的属性不一致则重置 skus 列表
|
|
||||||
if (!validateData(propertyList)) {
|
|
||||||
// 如果不一致则重置表数据,默认添加新的属性重新生成 sku 列表
|
|
||||||
formData.value!.skus = []
|
|
||||||
}
|
|
||||||
for (const item of buildList) {
|
|
||||||
const row = {
|
|
||||||
properties: Array.isArray(item) ? item : [item], // 如果只有一个属性的话返回的是一个 property 对象
|
|
||||||
price: 0,
|
|
||||||
marketPrice: 0,
|
|
||||||
costPrice: 0,
|
|
||||||
barCode: '',
|
|
||||||
picUrl: '',
|
|
||||||
stock: 0,
|
|
||||||
weight: 0,
|
|
||||||
volume: 0,
|
|
||||||
subCommissionFirstPrice: 0,
|
|
||||||
subCommissionSecondPrice: 0
|
|
||||||
}
|
|
||||||
// 如果存在属性相同的 sku 则不做处理
|
|
||||||
const index = formData.value!.skus.findIndex(
|
|
||||||
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
|
|
||||||
)
|
|
||||||
if (index !== -1) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
formData.value.skus.push(row)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 生成 skus 前置校验
|
|
||||||
*/
|
|
||||||
const validateData = (propertyList: any[]) => {
|
|
||||||
const skuPropertyIds = []
|
|
||||||
formData.value.skus.forEach((sku) =>
|
|
||||||
sku.properties
|
|
||||||
?.map((property) => property.propertyId)
|
|
||||||
.forEach((propertyId) => {
|
|
||||||
if (skuPropertyIds.indexOf(propertyId) === -1) {
|
|
||||||
skuPropertyIds.push(propertyId)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
const propertyIds = propertyList.map((item) => item.id)
|
|
||||||
return skuPropertyIds.length === propertyIds.length
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 构建所有排列组合 */
|
|
||||||
const build = (propertyValuesList: Property[][]) => {
|
|
||||||
if (propertyValuesList.length === 0) {
|
|
||||||
return []
|
|
||||||
} else if (propertyValuesList.length === 1) {
|
|
||||||
return propertyValuesList[0]
|
|
||||||
} else {
|
|
||||||
const result: Property[][] = []
|
|
||||||
const rest = build(propertyValuesList.slice(1))
|
|
||||||
for (let i = 0; i < propertyValuesList[0].length; i++) {
|
|
||||||
for (let j = 0; j < rest.length; j++) {
|
|
||||||
// 第一次不是数组结构,后面的都是数组结构
|
|
||||||
if (Array.isArray(rest[j])) {
|
|
||||||
result.push([propertyValuesList[0][i], ...rest[j]])
|
|
||||||
} else {
|
|
||||||
result.push([propertyValuesList[0][i], rest[j]])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 监听属性列表,生成相关参数和表头 */
|
|
||||||
watch(
|
|
||||||
() => props.propertyList,
|
|
||||||
(propertyList) => {
|
|
||||||
// 如果不是多规格则结束
|
|
||||||
if (!formData.value.specType) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 如果当前组件作为批量添加数据使用,则重置表数据
|
|
||||||
if (props.isBatch) {
|
|
||||||
skuList.value = [
|
|
||||||
{
|
|
||||||
price: 0,
|
|
||||||
marketPrice: 0,
|
|
||||||
costPrice: 0,
|
|
||||||
barCode: '',
|
|
||||||
picUrl: '',
|
|
||||||
stock: 0,
|
|
||||||
weight: 0,
|
|
||||||
volume: 0,
|
|
||||||
subCommissionFirstPrice: 0,
|
|
||||||
subCommissionSecondPrice: 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断代理对象是否为空
|
|
||||||
if (JSON.stringify(propertyList) === '[]') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 重置表头
|
|
||||||
tableHeaders.value = []
|
|
||||||
// 生成表头
|
|
||||||
propertyList.forEach((item, index) => {
|
|
||||||
// name加属性项index区分属性值
|
|
||||||
tableHeaders.value.push({ prop: `name${index}`, label: item.name })
|
|
||||||
})
|
|
||||||
|
|
||||||
// 如果回显的 sku 属性和添加的属性一致则不处理
|
|
||||||
if (validateData(propertyList)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 添加新属性没有属性值也不做处理
|
|
||||||
if (propertyList.some((item) => item.values.length === 0)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// 生成 table 数据,即 sku 列表
|
|
||||||
generateTableData(propertyList)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
// 暴露出生成 sku 方法,给添加属性成功时调用
|
|
||||||
defineExpose({ generateTableData })
|
|
||||||
</script>
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-tabs v-model="tabName" type="border-card" tab-position="top">
|
<el-tabs v-model="tabName" type="border-card" tab-position="top" v-loading="formLoading">
|
||||||
<el-tab-pane label="商品信息" name="basic">
|
<el-tab-pane label="商品信息" name="basic">
|
||||||
<el-form :model="form" ref="spuForm" :rules="rules" label-width="90px">
|
<el-form :model="form" ref="spuForm" :rules="rules" label-width="90px">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
@@ -11,11 +11,13 @@
|
|||||||
<el-col :span="8" :offset="0">
|
<el-col :span="8" :offset="0">
|
||||||
<el-form-item label="分类" prop="productCategory">
|
<el-form-item label="分类" prop="productCategory">
|
||||||
<el-cascader
|
<el-cascader
|
||||||
:options="opts.productCategory"
|
:options="opts.category"
|
||||||
v-model="form.productCategory"
|
v-model="form.productCategory"
|
||||||
placeholder="请选择分类"
|
placeholder="请选择分类"
|
||||||
|
:props="{ label: 'name', value: 'id' }"
|
||||||
filterable
|
filterable
|
||||||
show-all-levels
|
show-all-levels
|
||||||
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
@@ -24,9 +26,9 @@
|
|||||||
<el-select v-model="form.productBrand" placeholder="请选择品牌" filterable>
|
<el-select v-model="form.productBrand" placeholder="请选择品牌" filterable>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in opts.brand"
|
v-for="item in opts.brand"
|
||||||
:key="item.value"
|
:key="item.brandId"
|
||||||
:label="item.label"
|
:label="item.name"
|
||||||
:value="item.value"
|
:value="item.brandId"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -44,15 +46,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 +62,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,11 +111,16 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="col in form.specsList"
|
v-for="(col, index) in form.productSpecList"
|
||||||
:prop="col.id"
|
|
||||||
:key="col.id"
|
:key="col.id"
|
||||||
:label="col.name"
|
:label="col.name"
|
||||||
/>
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span style="font-weight: bold; color: #40aaff">
|
||||||
|
{{ row.properties[index]?.valueName }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column prop="price" label="销售价">
|
<el-table-column prop="price" label="销售价">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-input-number v-model="row.price" :min="0.01" :step="1" />
|
<el-input-number v-model="row.price" :min="0.01" :step="1" />
|
||||||
@@ -129,46 +136,67 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div class="mt-20px flex justify-center">
|
|
||||||
<el-button type="primary" @click="onSubmit">保存</el-button>
|
|
||||||
<el-button plain>重置</el-button>
|
|
||||||
</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" />
|
<div class="mt-20px flex justify-center">
|
||||||
|
<el-button type="primary" @click="onSubmit">保存</el-button>
|
||||||
|
<el-button plain>重置</el-button>
|
||||||
|
</div>
|
||||||
|
<ProductAttributesAddForm ref="attributesAddFormRef" :propertyList="form.productSpecList" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { cloneDeep } from 'lodash-es'
|
||||||
|
import * as PropertyApi from '@/api/mall/product/property'
|
||||||
|
import * as ProductApi from '@/api/mall/product/index'
|
||||||
import ProductAttributesAddForm from './Comp/ProductAttributesAddForm.vue'
|
import ProductAttributesAddForm from './Comp/ProductAttributesAddForm.vue'
|
||||||
|
import * as BrandApi from '@/api/mall/product/brand'
|
||||||
|
import * as CategoryApi from '@/api/mall/product/category'
|
||||||
|
import { handleTree } from '@/utils/tree'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
|
const { t } = useI18n() // 国际化
|
||||||
|
|
||||||
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,
|
||||||
|
status: 0
|
||||||
})
|
})
|
||||||
const rules = ref({})
|
const rules = {
|
||||||
|
productName: { required: true, message: '产品名称不可为空', trigger: 'blur' }
|
||||||
|
}
|
||||||
const attributesAddFormRef = ref() // 添加商品属性表单
|
const attributesAddFormRef = ref() // 添加商品属性表单
|
||||||
|
|
||||||
const opts = {
|
const opts = ref({
|
||||||
brand: [],
|
brand: [],
|
||||||
productCategory: []
|
category: []
|
||||||
|
})
|
||||||
|
|
||||||
|
async function getOptions() {
|
||||||
|
BrandApi.getSimpleBrandList().then((data) => {
|
||||||
|
opts.value.brand = data || []
|
||||||
|
})
|
||||||
|
CategoryApi.getCategorySimpleList().then((data) => {
|
||||||
|
opts.value.category = handleTree(data || [])
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除属性*/
|
/** 删除属性*/
|
||||||
function handleCloseProperty(index) {
|
function handleCloseProperty(index) {
|
||||||
form.value.specsList?.splice(index, 1)
|
form.value.productSpecList?.splice(index, 1)
|
||||||
|
getTableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
const attributeIndex = ref(null)
|
const attributeIndex = ref(null)
|
||||||
@@ -184,9 +212,8 @@ async function handleInputConfirm(index, propertyId) {
|
|||||||
if (inputValue.value) {
|
if (inputValue.value) {
|
||||||
// 保存属性值
|
// 保存属性值
|
||||||
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)
|
form.value.productSpecList[index].values.push({ id, name: inputValue.value })
|
||||||
form.value.specsList[index].values.push({ id, name: inputValue.value })
|
|
||||||
message.success('添加成功')
|
message.success('添加成功')
|
||||||
} catch {
|
} catch {
|
||||||
message.error('添加失败,请重试')
|
message.error('添加失败,请重试')
|
||||||
@@ -194,31 +221,88 @@ async function handleInputConfirm(index, propertyId) {
|
|||||||
}
|
}
|
||||||
attributeIndex.value = null
|
attributeIndex.value = null
|
||||||
inputValue.value = ''
|
inputValue.value = ''
|
||||||
form.value.skuList = getTableList()
|
getTableList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除属性值*/
|
||||||
|
function handleCloseValue(index, valueIndex) {
|
||||||
|
form.value.productSpecList[index].values?.splice(valueIndex, 1)
|
||||||
|
getTableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTableList() {
|
function getTableList() {
|
||||||
let list = []
|
const propertyList = [...form.value.productSpecList]
|
||||||
form.value.specsList.map((item) => {
|
// 构建数据结构
|
||||||
if (!list.length) {
|
const propertyValues = propertyList.map((item) =>
|
||||||
item.values.map((it) => {
|
item.values.map((v) => ({
|
||||||
const obj = {}
|
propertyId: item.id,
|
||||||
obj[it.id] = it.name
|
propertyName: item.name,
|
||||||
list.push(obj)
|
valueId: v.id,
|
||||||
})
|
valueName: v.name
|
||||||
} else {
|
}))
|
||||||
item.values.map((it, index) => {
|
)
|
||||||
if (index < list.length) {
|
const buildSkuList = build(propertyValues)
|
||||||
list[index][it.id] = it.name
|
// 如果回显的 sku 属性和添加的属性不一致则重置 skuList 列表
|
||||||
} else {
|
if (!validateData(propertyList)) {
|
||||||
const obj = {}
|
// 如果不一致则重置表数据,默认添加新的属性重新生成 sku 列表
|
||||||
obj[it.id] = it.name
|
// form.value.skuList = []
|
||||||
list.push(obj)
|
}
|
||||||
}
|
form.value.skuList = []
|
||||||
})
|
for (const item of buildSkuList) {
|
||||||
|
const row = {
|
||||||
|
properties: Array.isArray(item) ? item : [item], // 如果只有一个属性的话返回的是一个 property 对象
|
||||||
|
price: 0.01,
|
||||||
|
intro: '',
|
||||||
|
specsName: ''
|
||||||
}
|
}
|
||||||
})
|
// 如果存在属性相同的 sku 则不做处理
|
||||||
return list
|
const index = form.value.skuList.findIndex(
|
||||||
|
(sku) => JSON.stringify(sku.properties) === JSON.stringify(row.properties)
|
||||||
|
)
|
||||||
|
if (index !== -1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
form.value.skuList.push(row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成 skuList 前置校验
|
||||||
|
*/
|
||||||
|
const validateData = (propertyList) => {
|
||||||
|
const skuPropertyIds = []
|
||||||
|
form.value.skuList.forEach((sku) =>
|
||||||
|
sku.properties.map((property) => {
|
||||||
|
if (skuPropertyIds.indexOf(property.propertyId) === -1) {
|
||||||
|
skuPropertyIds.push(property.propertyId)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
const propertyIds = propertyList.map((item) => item.id)
|
||||||
|
return skuPropertyIds.length === propertyIds.length
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 构建所有排列组合 */
|
||||||
|
const build = (propertyValuesList) => {
|
||||||
|
if (propertyValuesList.length === 0) {
|
||||||
|
return []
|
||||||
|
} else if (propertyValuesList.length === 1) {
|
||||||
|
return propertyValuesList[0]
|
||||||
|
} else {
|
||||||
|
const result = []
|
||||||
|
const rest = build(propertyValuesList.slice(1))
|
||||||
|
for (let i = 0; i < propertyValuesList[0].length; i++) {
|
||||||
|
for (let j = 0; j < rest.length; j++) {
|
||||||
|
// 第一次不是数组结构,后面的都是数组结构
|
||||||
|
if (Array.isArray(rest[j])) {
|
||||||
|
result.push([propertyValuesList[0][i], ...rest[j]])
|
||||||
|
} else {
|
||||||
|
result.push([propertyValuesList[0][i], rest[j]])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputRef = ref([]) //标签输入框Ref
|
const inputRef = ref([]) //标签输入框Ref
|
||||||
@@ -238,32 +322,152 @@ const setInputRef = (el) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleAddSpec() {
|
function handleAddSpec() {
|
||||||
const id = parseInt(Math.random() * 1000)
|
attributesAddFormRef.value.open()
|
||||||
form.value.specsList.push({ name: `测试规格${id}`, id, values: [] })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const spuForm = ref()
|
||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
message.success('保存成功!')
|
spuForm.value.validate(async (valid) => {
|
||||||
|
if (valid && validateSku()) {
|
||||||
|
// 深拷贝一份, 这样最终 server 端不满足,不需要影响原始数据
|
||||||
|
const deepCopyFormData = cloneDeep(unref(form.value))
|
||||||
|
deepCopyFormData.productSpecList = deepCopyFormData.skuList
|
||||||
|
if (deepCopyFormData.productCategory && deepCopyFormData.productCategory.length) {
|
||||||
|
deepCopyFormData.productCategory = deepCopyFormData.productCategory.at(-1)
|
||||||
|
}
|
||||||
|
delete deepCopyFormData.skuList
|
||||||
|
// 校验都通过后提交表单
|
||||||
|
const data = deepCopyFormData
|
||||||
|
const id = route.query.id
|
||||||
|
if (!id) {
|
||||||
|
await ProductApi.createProduct(data)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
} else {
|
||||||
|
await ProductApi.updateProduct(data)
|
||||||
|
message.success(t('common.updateSuccess'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
// 作为活动组件的校验
|
||||||
|
const ruleConfig = [
|
||||||
|
{
|
||||||
|
name: 'specsName',
|
||||||
|
rule: (arg) => arg.length,
|
||||||
|
message: '规格名称不可为空 !!!'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'price',
|
||||||
|
rule: (arg) => arg >= 0.01,
|
||||||
|
message: '商品销售价格必须大于等于 0.01 元!!!'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存时,每个商品规格的表单要校验下。例如说,销售金额最低是 0.01 这种。
|
||||||
|
*/
|
||||||
|
const validateSku = () => {
|
||||||
|
let warningInfo = '请检查商品各行相关属性配置,'
|
||||||
|
let validate = form.value.skuList.length > 0 // 默认通过
|
||||||
|
if (!form.value.skuList.length) {
|
||||||
|
message.info('请添加商品规格!!!')
|
||||||
|
}
|
||||||
|
for (const sku of form.value.skuList) {
|
||||||
|
for (const rule of ruleConfig) {
|
||||||
|
const arg = getValue(sku, rule.name)
|
||||||
|
if (!rule.rule(arg)) {
|
||||||
|
validate = false // 只要有一个不通过则直接不通过
|
||||||
|
warningInfo += rule.message
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 只要有一个不通过则结束后续的校验
|
||||||
|
if (!validate) {
|
||||||
|
message.warning(warningInfo)
|
||||||
|
throw new Error(warningInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return validate
|
||||||
|
}
|
||||||
|
|
||||||
|
const getValue = (obj, arg) => {
|
||||||
|
const keys = arg.split('.')
|
||||||
|
let value = obj
|
||||||
|
for (const key of keys) {
|
||||||
|
if (value && typeof value === 'object' && key in value) {
|
||||||
|
value = value[key]
|
||||||
|
} else {
|
||||||
|
value = undefined
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
const formLoading = ref(false)
|
||||||
|
|
||||||
|
/** 获得详情 */
|
||||||
|
const getDetail = async () => {
|
||||||
if (route.query?.id) {
|
if (route.query?.id) {
|
||||||
form.value = {
|
formLoading.value = true
|
||||||
name: '商品名称哦~'
|
try {
|
||||||
|
const res = await ProductApi.getProduct(route.query.id)
|
||||||
|
const propList = getPropertyList(res.productSpecList)
|
||||||
|
form.value = {
|
||||||
|
...res,
|
||||||
|
skuList: res.productSpecList,
|
||||||
|
productSpecList: propList
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
} 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,
|
||||||
|
status: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得商品的规格列表 - 商品相关的公共函数
|
||||||
|
*
|
||||||
|
* @param spu
|
||||||
|
* @return PropertyAndValues 规格列表
|
||||||
|
*/
|
||||||
|
const getPropertyList = (list) => {
|
||||||
|
// 直接拿返回的 skus 属性逆向生成出 propertyList
|
||||||
|
const properties = []
|
||||||
|
// 只有是多规格才处理
|
||||||
|
list.forEach((sku) => {
|
||||||
|
sku.properties?.forEach(({ propertyId, propertyName, valueId, valueName }) => {
|
||||||
|
// 添加属性
|
||||||
|
if (!properties?.some((item) => item.id === propertyId)) {
|
||||||
|
properties.push({ id: propertyId, name: propertyName, values: [] })
|
||||||
|
}
|
||||||
|
// 添加属性值
|
||||||
|
const index = properties?.findIndex((item) => item.id === propertyId)
|
||||||
|
if (!properties[index].values?.some((value) => value.id === valueId)) {
|
||||||
|
properties[index].values?.push({ id: valueId, name: valueName })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return properties
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
getOptions()
|
||||||
|
await getDetail()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
>
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in opts.brand"
|
v-for="item in opts.brand"
|
||||||
:key="item.value"
|
:key="item.brandId"
|
||||||
:label="item.label"
|
:label="item.name"
|
||||||
:value="item.value"
|
:value="item.brandId"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -33,14 +33,17 @@
|
|||||||
placeholder="请选择分类"
|
placeholder="请选择分类"
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
|
:props="{ label: 'name', value: 'id', checkStrictly: true }"
|
||||||
show-all-levels
|
show-all-levels
|
||||||
@change="handleQuery"
|
@change="handleQuery"
|
||||||
/>
|
/>
|
||||||
</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(null)" v-hasPermi="['mall:prod:add']">
|
||||||
|
新增
|
||||||
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
@@ -51,6 +54,16 @@
|
|||||||
<div class="pl-100px pr-100px">
|
<div class="pl-100px pr-100px">
|
||||||
<el-table :data="row.productSpecList">
|
<el-table :data="row.productSpecList">
|
||||||
<el-table-column label="规格名称" prop="specsName" />
|
<el-table-column label="规格名称" prop="specsName" />
|
||||||
|
<el-table-column />
|
||||||
|
<el-table-column
|
||||||
|
v-for="(item, index) in row.properties"
|
||||||
|
:key="index"
|
||||||
|
:label="item.label"
|
||||||
|
>
|
||||||
|
<template #default="scope">
|
||||||
|
{{ scope.row.properties[index].valueName }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="售价" prop="price" />
|
<el-table-column label="售价" prop="price" />
|
||||||
<el-table-column label="简介" prop="intro" />
|
<el-table-column label="简介" prop="intro" />
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -71,8 +84,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>
|
||||||
@@ -89,6 +116,9 @@
|
|||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
import { createImageViewer } from '@/components/ImageViewer'
|
import { createImageViewer } from '@/components/ImageViewer'
|
||||||
import * as ProductApi from '@/api/mall/product'
|
import * as ProductApi from '@/api/mall/product'
|
||||||
|
import * as BrandApi from '@/api/mall/product/brand'
|
||||||
|
import * as CategoryApi from '@/api/mall/product/category'
|
||||||
|
import { handleTree } from '@/utils/tree'
|
||||||
|
|
||||||
const { currentRoute, push } = useRouter()
|
const { currentRoute, push } = useRouter()
|
||||||
const message = useMessage() // 消息弹窗
|
const message = useMessage() // 消息弹窗
|
||||||
@@ -127,8 +157,18 @@ async function handleDelete(id) {
|
|||||||
async function getList() {
|
async function getList() {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await ProductApi.getProductPage(queryParams.value)
|
const params = { ...queryParams.value }
|
||||||
tableList.value = data.list
|
if (params.productCategory && params.productCategory.length) {
|
||||||
|
params.productCategory = params.productCategory.at(-1)
|
||||||
|
}
|
||||||
|
const data = await ProductApi.getProductPage(params)
|
||||||
|
tableList.value = data.list.map((prod) => ({
|
||||||
|
...prod,
|
||||||
|
properties: prod.productSpecList[0].properties.map((item) => ({
|
||||||
|
propertyId: item.propertyId,
|
||||||
|
label: item.propertyName
|
||||||
|
}))
|
||||||
|
}))
|
||||||
total.value = data.total
|
total.value = data.total
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
@@ -147,6 +187,15 @@ function handleQuery() {
|
|||||||
getList()
|
getList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function getOptions() {
|
||||||
|
BrandApi.getSimpleBrandList().then((data) => {
|
||||||
|
opts.value.brand = data || []
|
||||||
|
})
|
||||||
|
CategoryApi.getCategorySimpleList().then((data) => {
|
||||||
|
opts.value.productCategory = handleTree(data || [])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/** 重置按钮操作 */
|
/** 重置按钮操作 */
|
||||||
function resetQuery() {
|
function resetQuery() {
|
||||||
queryParams.value = {
|
queryParams.value = {
|
||||||
@@ -166,7 +215,7 @@ function resetQuery() {
|
|||||||
*/
|
*/
|
||||||
function openForm(id) {
|
function openForm(id) {
|
||||||
// 修改
|
// 修改
|
||||||
if (typeof id == 'number') {
|
if (id) {
|
||||||
push({
|
push({
|
||||||
path: '/miniMall/productEdit',
|
path: '/miniMall/productEdit',
|
||||||
query: { id }
|
query: { id }
|
||||||
@@ -185,6 +234,10 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
handleQuery()
|
handleQuery()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getOptions()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog title="发起采购" v-model="dialogVisible" width="800px">
|
<Dialog title="发起采购" v-model="dialogVisible" width="800px">
|
||||||
<el-form :model="form" ref="addForm" :rules="rules" label-width="100px">
|
<el-form :model="form" ref="formRef" :rules="rules" label-width="100px">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12" :offset="0">
|
<el-col :span="12" :offset="0">
|
||||||
<el-form-item label="产品" prop="product">
|
<el-form-item label="产品" prop="productId">
|
||||||
<el-select v-model="form.product" placeholder="请选择" filterable style="width: 100%">
|
<el-select v-model="form.productId" placeholder="请选择" filterable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in productOptions"
|
v-for="item in props.opts.product"
|
||||||
:key="item.value"
|
:key="item.productId"
|
||||||
:label="item.label"
|
:label="item.productName"
|
||||||
:value="item.value"
|
:value="item.productId"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</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="specs">
|
<el-form-item label="规格" prop="specsId">
|
||||||
<el-select v-model="form.specs" placeholder="请选择" filterable style="width: 100%">
|
<el-select v-model="form.specsId" placeholder="请选择" filterable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in specsOptions"
|
v-for="item in specOptions"
|
||||||
:key="item.value"
|
:key="item.specsId"
|
||||||
:label="item.label"
|
:label="item.specsName"
|
||||||
:value="item.value"
|
:value="item.specsId"
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -30,34 +30,41 @@
|
|||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="12" :offset="0">
|
<el-col :span="12" :offset="0">
|
||||||
<el-form-item label="供应商" prop="supplier">
|
<el-form-item label="供应商" prop="supplier">
|
||||||
<el-input v-model="form.supplier" placeholder="请输入" />
|
<el-select v-model="form.supplier" placeholder="选择供应商" filterable>
|
||||||
|
<el-option
|
||||||
|
v-for="item in opts.supplier"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
</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="purchaseCount">
|
<el-form-item label="采购数量" prop="num">
|
||||||
<el-input-number
|
<el-input-number :min="1" v-model="form.num" placeholder="请输入" style="width: 100%" />
|
||||||
:min="1"
|
|
||||||
v-model="form.purchaseCount"
|
|
||||||
placeholder="请输入"
|
|
||||||
style="width: 100%"
|
|
||||||
/>
|
|
||||||
</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="12" :offset="0">
|
<el-col :span="12" :offset="0">
|
||||||
<el-form-item label="采购单价" prop="price">
|
<el-form-item label="采购单价" prop="unitPrice">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
:min="0"
|
:min="0"
|
||||||
v-model="form.price"
|
v-model="form.unitPrice"
|
||||||
placeholder="请输入"
|
placeholder="请输入"
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
</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="warehouse">
|
<el-form-item label="存放仓库" prop="warehouseId">
|
||||||
<el-select v-model="form.warehouse" placeholder="请选择" filterable style="width: 100%">
|
<el-select
|
||||||
|
v-model="form.warehouseId"
|
||||||
|
placeholder="请选择"
|
||||||
|
filterable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in warehouseOptions"
|
v-for="item in warehouseOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -86,13 +93,42 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import * as PurchaseApi from '@/api/mall/purchase'
|
||||||
|
const props = defineProps({
|
||||||
|
opts: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
product: [],
|
||||||
|
spec: [],
|
||||||
|
supplier: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const specOptions = computed(() => {
|
||||||
|
if (form.value.productId) {
|
||||||
|
return (
|
||||||
|
props.opts.product.find((it) => it.productId == form.value.productId)?.productSpecList || []
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
|
||||||
const form = ref()
|
const form = ref({})
|
||||||
const rules = ref({})
|
const rules = {
|
||||||
|
productId: { required: true, message: '产品不可为空', trigger: 'change' },
|
||||||
|
specsId: { required: true, message: '规格不可为空', trigger: 'change' },
|
||||||
|
supplier: { required: true, message: '供应商不可为空', trigger: 'change' },
|
||||||
|
warehouseId: { required: true, message: '仓库不可为空', trigger: 'change' },
|
||||||
|
num: { required: true, message: '采购数量不可为空', trigger: 'change,blur' },
|
||||||
|
unitPrice: { required: true, message: '采购单价不可为空', trigger: 'change,blur' }
|
||||||
|
}
|
||||||
|
|
||||||
const productOptions = ref([])
|
|
||||||
const specsOptions = ref([])
|
|
||||||
const warehouseOptions = ref([
|
const warehouseOptions = ref([
|
||||||
{ label: '自营仓', value: 1 },
|
{ label: '自营仓', value: 1 },
|
||||||
{ label: '供应商仓', value: 2 }
|
{ label: '供应商仓', value: 2 }
|
||||||
@@ -100,19 +136,37 @@ const warehouseOptions = ref([
|
|||||||
|
|
||||||
const open = (val) => {
|
const open = (val) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
form.value = val || {
|
form.value = { ...val } || {
|
||||||
product: '',
|
productId: undefined,
|
||||||
specs: '',
|
specsId: undefined,
|
||||||
supplier: '',
|
supplier: undefined,
|
||||||
purchaseCount: 1,
|
num: undefined,
|
||||||
warehouse: 1,
|
unitPrice: undefined,
|
||||||
remark: ''
|
warehouseId: undefined,
|
||||||
|
remark: undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
function handleSave() {
|
const formRef = ref()
|
||||||
console.log('保存')
|
const formLoading = ref(false)
|
||||||
|
|
||||||
|
async function handleSave() {
|
||||||
|
// 校验表单
|
||||||
|
if (!formRef.value) return
|
||||||
|
const valid = await formRef.value.validate()
|
||||||
|
if (!valid) return
|
||||||
|
// 提交请求
|
||||||
|
formLoading.value = true
|
||||||
|
try {
|
||||||
|
await PurchaseApi.createPurchase(form.value)
|
||||||
|
message.success(t('common.createSuccess'))
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
formLoading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog title="采购审核" v-model="dialogVisible" width="800px">
|
<Dialog title="采购审核" v-model="dialogVisible" width="800px">
|
||||||
<Descriptions :data="form" :schema="schema" :columns="3" />
|
<Descriptions :data="form" :schema="schema" :columns="3" />
|
||||||
<el-form v-if="canEdit" :model="form" ref="auditForm" :rules="rules" label-width="80px">
|
<el-form v-if="canEdit" :model="form" ref="auditForm" label-width="80px">
|
||||||
<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="status">
|
<el-form-item label="审核" prop="isPass">
|
||||||
<el-radio-group v-model="form.status">
|
<el-radio-group v-model="form.isPass">
|
||||||
<el-radio :label="2">通过</el-radio>
|
<el-radio :label="2">通过</el-radio>
|
||||||
<el-radio :label="3">驳回</el-radio>
|
<el-radio :label="3">驳回</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
@@ -29,74 +29,31 @@
|
|||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup name="DialogAuditPurchase">
|
||||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
|
||||||
const form = ref({})
|
const form = ref({})
|
||||||
const rules = ref({})
|
|
||||||
const canEdit = ref(true)
|
const canEdit = ref(true)
|
||||||
|
|
||||||
const schema = ref([
|
const schema = ref([])
|
||||||
{
|
|
||||||
field: 'name',
|
|
||||||
label: '产品名称'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'specsName',
|
|
||||||
label: '规格名称'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'supplier',
|
|
||||||
label: '供应商'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'purchaseCount',
|
|
||||||
label: '采购数量'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'unitPrice',
|
|
||||||
label: '采购单价'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'totalPrice',
|
|
||||||
label: '总金额'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'warehouse',
|
|
||||||
label: '仓库'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'warehouse',
|
|
||||||
label: '申请人'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: '',
|
|
||||||
label: '申请时间'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: 'remark',
|
|
||||||
label: '备注',
|
|
||||||
isEditor: true,
|
|
||||||
span: 3
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
const open = (val, flag) => {
|
const open = (val, flag) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
form.value = { ...val, status: 2, remark: '<p style="color: red;">哈哈哈</p>' }
|
form.value = { ...val }
|
||||||
canEdit.value = flag
|
canEdit.value = flag
|
||||||
|
resetSchema()
|
||||||
if (!canEdit.value) {
|
if (!canEdit.value) {
|
||||||
const arr = [
|
const arr = [
|
||||||
{
|
{
|
||||||
field: 'status',
|
field: 'auditStatusName',
|
||||||
label: '采购状态'
|
label: '采购状态'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'auditUser',
|
field: 'checkUserName',
|
||||||
label: '审核人'
|
label: '审核人'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: 'auditTime',
|
field: 'checkTime',
|
||||||
label: '审核时间'
|
label: '审核时间'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -107,10 +64,61 @@ const open = (val, flag) => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
schema.value = [...schema.value, ...arr]
|
schema.value = [...schema.value, ...arr]
|
||||||
|
} else {
|
||||||
|
form.value.isPass = 2
|
||||||
|
form.value.auditRemark = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
|
const resetSchema = () => {
|
||||||
|
schema.value = [
|
||||||
|
{
|
||||||
|
field: 'productName',
|
||||||
|
label: '产品名称'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'specsName',
|
||||||
|
label: '规格名称'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'supplier',
|
||||||
|
label: '供应商'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'num',
|
||||||
|
label: '采购数量'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'unitPrice',
|
||||||
|
label: '采购单价'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'totalPrice',
|
||||||
|
label: '总金额'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'warehouseName',
|
||||||
|
label: '仓库'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'applyUserName',
|
||||||
|
label: '申请人'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'applyTime',
|
||||||
|
label: '申请时间',
|
||||||
|
dateFormat: 'YYYY-MM-DD'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'remark',
|
||||||
|
label: '备注',
|
||||||
|
isEditor: true,
|
||||||
|
span: 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
function handleSave() {
|
function handleSave() {
|
||||||
console.log('保存')
|
console.log('保存')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// import { CrudSchema } from '@/hooks/web/useCrudSchemas'
|
|
||||||
import { dateFormatter } from '@/utils/formatTime'
|
import { dateFormatter } from '@/utils/formatTime'
|
||||||
|
|
||||||
const statusOptions = [
|
const statusOptions = [
|
||||||
@@ -7,11 +6,10 @@ const statusOptions = [
|
|||||||
{ label: '已驳回', value: 3 }
|
{ label: '已驳回', value: 3 }
|
||||||
]
|
]
|
||||||
|
|
||||||
// CrudSchema:https://doc.iocoder.cn/vue3/crud-schema/
|
|
||||||
const crudSchemas = reactive([
|
const crudSchemas = reactive([
|
||||||
{
|
{
|
||||||
label: '产品名称',
|
label: '产品名称',
|
||||||
field: 'name',
|
field: 'productName',
|
||||||
isSearch: true,
|
isSearch: true,
|
||||||
isTable: true
|
isTable: true
|
||||||
},
|
},
|
||||||
@@ -29,7 +27,7 @@ const crudSchemas = reactive([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '采购数量',
|
label: '采购数量',
|
||||||
field: 'purchaseCount',
|
field: 'num',
|
||||||
isSearch: false,
|
isSearch: false,
|
||||||
isTable: true
|
isTable: true
|
||||||
},
|
},
|
||||||
@@ -47,7 +45,7 @@ const crudSchemas = reactive([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '仓库',
|
label: '仓库',
|
||||||
field: 'warehouse',
|
field: 'warehouseName',
|
||||||
isSearch: false,
|
isSearch: false,
|
||||||
isTable: true
|
isTable: true
|
||||||
},
|
},
|
||||||
@@ -79,13 +77,13 @@ const crudSchemas = reactive([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '审核人',
|
label: '审核人',
|
||||||
field: 'auditUserName',
|
field: 'checkUserName',
|
||||||
isSearch: true,
|
isSearch: true,
|
||||||
isTable: true
|
isTable: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '审核时间',
|
label: '审核时间',
|
||||||
field: 'auditTime',
|
field: 'checkTime',
|
||||||
isSearch: true,
|
isSearch: true,
|
||||||
isTable: true,
|
isTable: true,
|
||||||
formatter: dateFormatter,
|
formatter: dateFormatter,
|
||||||
@@ -105,7 +103,7 @@ const crudSchemas = reactive([
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '采购状态',
|
label: '采购状态',
|
||||||
field: 'status',
|
field: 'auditStatus',
|
||||||
isSearch: true,
|
isSearch: true,
|
||||||
isTable: true,
|
isTable: true,
|
||||||
search: {
|
search: {
|
||||||
@@ -119,7 +117,7 @@ const crudSchemas = reactive([
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
table: {
|
table: {
|
||||||
field: 'statusName',
|
field: 'auditStatusName',
|
||||||
fixed: 'right'
|
fixed: 'right'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,25 +1,76 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<!-- 搜索工作栏 -->
|
<el-form inline :model="queryParams" class="-mb-15px" label-width="0">
|
||||||
<Search
|
<el-form-item>
|
||||||
:schema="allSchemas.searchSchema"
|
<el-select
|
||||||
labelWidth="0"
|
v-model="queryParams.productId"
|
||||||
expand
|
placeholder="选择产品"
|
||||||
expand-field="supplier"
|
clearable
|
||||||
@search="setSearchParams"
|
filterable
|
||||||
@reset="setSearchParams"
|
@change="changeProd"
|
||||||
>
|
>
|
||||||
<template #actionMore>
|
<el-option
|
||||||
<el-button type="primary" @click="handleAdd">发起采购</el-button>
|
v-for="item in opts.product"
|
||||||
</template>
|
:key="item.productId"
|
||||||
</Search>
|
:label="item.productName"
|
||||||
|
:value="item.productId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.specsId"
|
||||||
|
placeholder="选择规格"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:disabled="!queryParams.productId"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in opts.spec"
|
||||||
|
:key="item.specsId"
|
||||||
|
:label="item.specsName"
|
||||||
|
:value="item.specsId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-select v-model="queryParams.supplier" placeholder="供应商" clearable filterable>
|
||||||
|
<el-option
|
||||||
|
v-for="item in opts.supplier"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.applyTime"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="申请时间"
|
||||||
|
end-placeholder="申请时间"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="queryParams.checkTime"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="审核时间"
|
||||||
|
end-placeholder="审核时间"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<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>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
<!-- 列表 -->
|
<!-- 列表 -->
|
||||||
<SSTable
|
<el-table v-loading="loading" class="mt-20px" :data="tableList" border>
|
||||||
class="mt-20px"
|
|
||||||
v-model:tableObject="tableObject"
|
|
||||||
:tableColumns="allSchemas.tableColumns"
|
|
||||||
@get-list="getTableList"
|
|
||||||
>
|
|
||||||
<el-table-column
|
<el-table-column
|
||||||
v-for="item in allSchemas.tableColumns"
|
v-for="item in allSchemas.tableColumns"
|
||||||
:key="item.table?.field || item.field"
|
:key="item.table?.field || item.field"
|
||||||
@@ -27,52 +78,122 @@
|
|||||||
:label="item.label"
|
:label="item.label"
|
||||||
:fixed="item.fixed"
|
:fixed="item.fixed"
|
||||||
min-width="150px"
|
min-width="150px"
|
||||||
|
:formatter="item.formatter"
|
||||||
showOverflowTooltip
|
showOverflowTooltip
|
||||||
/>
|
/>
|
||||||
<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 v-else type="primary" link @click="purchaseAgain(row)">再次采购</el-button>
|
采购审核
|
||||||
<el-button type="primary" link @click="handleDetail(row)">详情</el-button>
|
</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>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</SSTable>
|
</el-table>
|
||||||
<DialogAdd ref="creatPurchase" />
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
:total="total"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DialogAdd :opts="opts" ref="creatPurchase" />
|
||||||
<DialogAudit ref="auditPurchase" />
|
<DialogAudit ref="auditPurchase" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup name="Purchase">
|
||||||
|
import * as ProductApi from '@/api/mall/product'
|
||||||
import { allSchemas } from './index.data.js'
|
import { allSchemas } from './index.data.js'
|
||||||
import DialogAdd from './Comp/DialogAdd.vue'
|
import DialogAdd from './Comp/DialogAdd.vue'
|
||||||
import DialogAudit from './Comp/DialogAudit.vue'
|
import DialogAudit from './Comp/DialogAudit.vue'
|
||||||
|
import * as PurchaseApi from '@/api/mall/purchase'
|
||||||
|
import { getDictOptions } from '@/utils/dict'
|
||||||
|
|
||||||
const creatPurchase = ref()
|
const creatPurchase = ref()
|
||||||
const auditPurchase = ref()
|
const auditPurchase = ref()
|
||||||
|
|
||||||
const tableObject = ref({
|
const opts = ref({
|
||||||
tableList: [],
|
product: [],
|
||||||
loading: false,
|
spec: [],
|
||||||
total: 1,
|
supplier: []
|
||||||
pageSize: 20,
|
|
||||||
currentPage: 1
|
|
||||||
})
|
})
|
||||||
const setSearchParams = function () {
|
|
||||||
tableObject.value.tableList = [
|
const tableList = ref([])
|
||||||
{ name: '测试', status: 1, statusName: '审核中', supplier: '林氏木业', purchaseCount: 10 },
|
const loading = ref(false)
|
||||||
{ name: '测试2', status: 2, statusName: '已通过', supplier: '张氏木业', purchaseCount: 1 },
|
const total = ref(0)
|
||||||
{ name: '测试3', status: 3, statusName: '已驳回', supplier: '周氏木业', purchaseCount: 5 }
|
|
||||||
]
|
const queryParams = ref({
|
||||||
|
productId: undefined,
|
||||||
|
specsId: undefined,
|
||||||
|
supplier: undefined,
|
||||||
|
applyTime: [],
|
||||||
|
checkTime: [],
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 20
|
||||||
|
})
|
||||||
|
|
||||||
|
function getOptions() {
|
||||||
|
ProductApi.getSimpleProductList().then((data) => {
|
||||||
|
opts.value.product = data
|
||||||
|
})
|
||||||
|
opts.value.supplier = getDictOptions('erp_supplier')
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTableList() {
|
function changeProd(val) {
|
||||||
tableObject.value.tableList = [
|
if (val) {
|
||||||
{ name: '测试', status: 1, statusName: '审核中', supplier: '林氏木业', purchaseCount: 10 },
|
opts.value.spec = opts.value.product.find((it) => it.productId == val).productSpecList
|
||||||
{ name: '测试2', status: 2, statusName: '已通过', supplier: '张氏木业', purchaseCount: 1 },
|
} else {
|
||||||
{ name: '测试3', status: 3, statusName: '已驳回', supplier: '周氏木业', purchaseCount: 5 }
|
opts.value.spec = []
|
||||||
]
|
}
|
||||||
|
queryParams.value.specsId = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetQuery() {
|
||||||
|
queryParams.value = {
|
||||||
|
productId: undefined,
|
||||||
|
specsId: undefined,
|
||||||
|
supplier: undefined,
|
||||||
|
applyTime: [],
|
||||||
|
checkTime: [],
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 20
|
||||||
|
}
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getList = async function () {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await PurchaseApi.getPurchasePage(queryParams.value)
|
||||||
|
tableList.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function purchaseAgain(row) {
|
function purchaseAgain(row) {
|
||||||
@@ -89,6 +210,11 @@ function handleAdd() {
|
|||||||
function handleDetail(row) {
|
function handleDetail(row) {
|
||||||
auditPurchase.value.open({ ...row }, false)
|
auditPurchase.value.open({ ...row }, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getOptions()
|
||||||
|
getList()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
Reference in New Issue
Block a user