同步 admin端稿件管理代码

This commit is contained in:
YuanFeng 2024-09-05 21:59:51 +08:00
parent dc46a919f5
commit aa9f2504f0
50 changed files with 3825 additions and 756 deletions

View File

@ -23,11 +23,15 @@ const setDefaultTheme = () => {
appStore.setIsDark(isDarkTheme)
}
setDefaultTheme()
import { getTeleport } from '@antv/x6-vue-shape';
// TeleportContainer
const TeleportContainer = defineComponent(getTeleport());
</script>
<template>
<ConfigGlobal :size="currentSize">
<RouterView :class="greyMode ? `${prefixCls}-grey-mode` : ''" />
<routerSearch />
<TeleportContainer />
</ConfigGlobal>
</template>
<style lang="scss">

View File

@ -1,5 +1,4 @@
import request from '@/config/axios'
import {getSimpleMenusList} from "@/api/system/menu";
// 品牌管理 VO
export interface BrandVO {
@ -15,8 +14,6 @@ export interface BrandVO {
}
// 品牌管理 API
export const BrandApi = {
// 查询品牌管理 分页
@ -29,17 +26,17 @@ export const BrandApi = {
return await request.get({ url: `/oms/brand/get?id=` + id })
},
// 新增品牌管理
// 新增品牌管理
createBrand: async (data: BrandVO) => {
return await request.post({ url: `/oms/brand/create`, data })
},
// 修改品牌管理
// 修改品牌管理
updateBrand: async (data: BrandVO) => {
return await request.put({ url: `/oms/brand/update`, data })
},
// 删除品牌管理
// 删除品牌管理
deleteBrand: async (id: number) => {
return await request.delete({ url: `/oms/brand/delete?id=` + id })
},
@ -50,7 +47,8 @@ export const BrandApi = {
},
}
// 查询品牌(精简)列表
export const getSimpleBrandList = async () => {
return await request.get({ url: `/oms/brand/simple-list`})
// 获取品牌精简信息列表
export const getSimpleBrandList = (): Promise<BrandVO[]> => {
return request.get({ url: '/oms/app/brand/simple-list' })
}

View File

@ -0,0 +1,47 @@
import request from '@/config/axios'
// 产品保养项 VO
export interface ProductCareItemVO {
id: number // id
value: string // 说明
iconUrl: string // icon图标
brandIds: string // 品牌
isCombo: boolean // 只作用于组合
isAll: boolean // 品牌通用
locale: string // 语言标识 字典-language_locale
enabled: boolean // 启用状态
remark: string // 备注
}
// 产品保养项 API
export const ProductCareItemApi = {
// 查询产品保养项 分页
getProductCareItemPage: async (params: any) => {
return await request.get({ url: `/oms/product-care-item/page`, params })
},
// 查询产品保养项 详情
getProductCareItem: async (id: number) => {
return await request.get({ url: `/oms/product-care-item/get?id=` + id })
},
// 新增产品保养项
createProductCareItem: async (data: ProductCareItemVO) => {
return await request.post({ url: `/oms/product-care-item/create`, data })
},
// 修改产品保养项
updateProductCareItem: async (data: ProductCareItemVO) => {
return await request.put({ url: `/oms/product-care-item/update`, data })
},
// 删除产品保养项
deleteProductCareItem: async (id: number) => {
return await request.delete({ url: `/oms/product-care-item/delete?id=` + id })
},
// 导出产品保养项 Excel
exportProductCareItem: async (params) => {
return await request.download({ url: `/oms/product-care-item/export-excel`, params })
},
}

View File

@ -0,0 +1,69 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<Form :disabled="disabled" ref="formRef" :schema="allSchemas.formSchema" :rules="rules" v-loading="formLoading" />
<template #footer>
<el-button v-if="!disabled" @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { BrandApi, BrandVO } from '@/api/oms/brand'
import { rules, allSchemas } from './config.data'
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formRef = ref() // Ref
const disabled = computed(() => {
return formType.value !== 'create' && formType.value !== 'update'
})
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
//
if (id) {
formLoading.value = true
try {
const data = await BrandApi.getBrand(id)
formRef.value.setValues(data)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.getElFormRef().validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formRef.value.formModel as BrandVO
if (formType.value === 'create') {
await BrandApi.createBrand(data)
message.success(t('common.createSuccess'))
} else {
await BrandApi.updateBrand(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
</script>

View File

@ -0,0 +1,102 @@
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
import { dateFormatter } from '@/utils/formatTime'
// 表单校验
export const rules = reactive({
code: [required],
name: [required],
})
// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/
const crudSchemas = reactive<CrudSchema[]>([
{
label: 'id',
field: 'id',
isForm: false,
},
{
label: '系统编码',
field: 'code',
isSearch: true,
},
{
label: '名称',
field: 'name',
isSearch: true,
},
{
label: 'logo',
field: 'logo',
isSearch: true,
form: {
component: 'UploadImg',
componentProps:{
disabled: true
},
},
},
{
label: '品牌领域',
field: 'brandField',
dictType: DICT_TYPE.BRAND_INDUSTRY_FIELD,
dictClass: 'string',
isSearch: true,
form: {
component: 'SelectV2'
},
},
{
label: '官网',
field: 'website',
},
{
label: '品牌介绍',
field: 'intro',
isTable: false,
form: {
component: 'Editor',
componentProps: {
valueHtml: '',
height: 200
}
},
},
{
label: '语言标识',
field: 'locale',
dictType: DICT_TYPE.LANGUAGE_LOCALE,
dictClass: 'string',
search: {
show: true,
},
form: {
component: 'SelectV2'
},
},
{
label: '备注',
field: 'remark',
isTable: false,
},
{
label: '创建时间',
field: 'createTime',
formatter: dateFormatter,
isSearch: true,
search: {
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
type: 'daterange',
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
}
},
isForm: false,
},
{
label: '操作',
field: 'action',
isForm: false
}
])
export const { allSchemas } = useCrudSchemas(crudSchemas)

View File

@ -0,0 +1,293 @@
<template>
<slot>
<div>
<el-input
v-model="that.showValue"
clearable
:placeholder="props.placeholder"
@clear="clearData"
@click="viewDetails"
>
<template #append>
<el-button @click.stop="openDialog">
<Icon icon="ep:search"/>
</el-button>
</template>
</el-input>
</div>
</slot>
<Dialog
:title="dialogTitle"
width="80%"
v-model="that.visible"
@close="updateVisible(false)">
<div>
<!-- 搜索工作栏 -->
<ContentWrap>
<Search
:schema="allSchemas.searchSchema"
:is-col="false"
@search="setSearchParams"
@reset="setSearchParams">
<!-- 新增等操作按钮 -->
<template #actionMore>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['oms:draft-design-data:create']"
>
<Icon icon="ep:plus" class="mr-5px"/>
新增
</el-button>
</template>
</Search>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<Table
:columns="allSchemas.tableColumns"
:data="tableObject.tableList"
:loading="tableObject.loading"
:selection="true"
ref="tableRef"
@selection-change="selectionChange"
:pagination="{
total: tableObject.total
}"
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
>
<template #action="{ row }">
<el-button
link
type="primary"
@click="openForm('update', row.id)"
v-hasPermi="['oms:draft-design-data:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
v-hasPermi="['oms:draft-design-data:delete']"
@click="handleDelete(row.id)"
>
删除
</el-button>
</template>
</Table>
</ContentWrap>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="updateVisible(false,true)">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ t('common.ok') }}</el-button>
</span>
</template>
</Dialog>
<!-- 表单弹窗添加/修改 -->
<DataForm ref="formRef" @success="getList"/>
</template>
<script lang="ts" setup name="BrandDataListDialog">
import {allSchemas} from './config.data'
import {BrandApi, BrandVO} from '@/api/oms/brand'
import DataForm from './DataForm.vue'
/** 稿件图片库 */
defineOptions({name: 'BrandDataListDialog'})
const emit = defineEmits(['update:modelValue', 'update:visible', 'submit']) // success
const {t} = useI18n() //
const dialogTitle = ref('设计稿件') //
const props = defineProps({
modelValue: {
type: [String],
required: true
},
dataKey: {
type: String,
required: false,
default: 'id'
},
showKey: {
type: String,
required: false,
default: 'name'
},
multiple: {
type: Boolean,
required: false,
default: false
},
placeholder: {
type: String,
required: false,
default: '请选择'
},
width: {
type: String,
required: false,
default: '64px'
},
height: {
type: String,
required: false,
default: '64px'
},
visible: {
type: Boolean,
required: false,
default: false
}
})
const that = reactive({
inputVal: '',
showValue: '',
visible: false,
})
const openDialog = () => {
updateVisible(true);
}
const viewDetails = () => {
if (that.inputVal) {
openForm("preview", that.inputVal.split(",")[0])
} else {
openDialog();
}
console.log("viewDetails", viewDetails)
}
watch(() => props.modelValue, (newVal) => {
that.inputVal = toStr(newVal);
if (that.inputVal) {
initInput();
}
})
const toStr = (data: any) => {
if (data !== null && data !== undefined) {
return `${data}`
}
return data
}
let map = new Map();
const initInput = async () => {
const dataKey = that.inputVal + ',' + props.dataKey + ',' + props.showKey + ',' + props.multiple;
if (map.has(dataKey)) {
const data = map.get(dataKey)
if (data) {
that.inputVal = data.inputVal
that.showValue = data.showValue
console.log('缓存数据', data)
return;
}
}
const ids = that.inputVal.split(",");
let tmpInput = [];
let tmpShow = [];
for (let i = 0; i < ids.length; i++) {
const data = await BrandApi.getBrand(ids[i])
tmpInput.push(data[props.dataKey]);
tmpShow.push(data[props.showKey]);
}
that.inputVal = tmpInput.join(',');
that.showValue = tmpShow.join(',');
map.set(dataKey, {
inputVal: that.inputVal,
showValue: that.showValue
})
}
watch(() => props.visible, (newVal) => {
that.visible = newVal;
})
const clearData = () => {
that.inputVal = '';
that.showValue = '';
updateValue();
}
const updateVisible = (visible: boolean, clearInput = false) => {
that.visible = visible;
emit("update:visible", visible)
if (clearInput) {
clearData();
}
}
defineExpose({}) // open
const submit = () => {
updateValue();
updateVisible(false)
}
const updateValue = () => {
emit("update:modelValue", that.inputVal)
}
//
const selectionChange = (row) => {
if (row && row.length > 0) {
if (props.multiple) {
that.inputVal = row.map(item => item[props.dataKey || 'id']).join(',')
that.showValue = row.map(item => item[props.showKey || 'id']).join(',')
} else {
if (row.length > 1) {
useMessage().warning('单选数据,已忽略其他')
}
that.showValue = row[row.length - 1][props.showKey || 'id']
that.inputVal = row[row.length - 1][props.dataKey || 'id']
}
} else {
that.inputVal = ''
that.showValue = ''
}
}
const {tableObject, tableMethods} = useTable({
getListApi: BrandApi.getBrandPage, //
delListApi: BrandApi.deleteBrand //
})
//
const {getList, setSearchParams} = tableMethods
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = (id: number) => {
tableMethods.delList(id, false)
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
position: relative;
.el-input__inner {
padding-right: 18px;
}
.el-input__suffix {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
}
}
</style>

View File

@ -0,0 +1,74 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<Form :disabled="disabled" ref="formRef" :schema="allSchemas.formSchema" :rules="rules" v-loading="formLoading" >
<template #enabled="form">
<el-radio-group v-model="form['enabled']">
<el-radio :label="true">启用</el-radio>
<el-radio :label="false">停用</el-radio>
</el-radio-group>
</template>
</Form>
<template #footer>
<el-button v-if="!disabled" @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import {DraftDesignDataApi} from "@/api/oms/draftdesigndata";
import { rules, allSchemas } from './config.data'
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formRef = ref() // Ref
const disabled = computed(() => {
return formType.value !== 'create' && formType.value !== 'update'
})
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
//
if (id) {
formLoading.value = true
try {
const data = await DraftDesignDataApi.getDraftDesignData(id)
formRef.value.setValues(data)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.getElFormRef().validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formRef.value.formModel as DraftDesignDataApi.DraftDesignDataVO
if (formType.value === 'create') {
await DraftDesignDataApi.createDraftDesignData(data)
message.success(t('common.createSuccess'))
} else {
await DraftDesignDataApi.updateDraftDesignData(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
</script>

View File

@ -0,0 +1,82 @@
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
import { dateFormatter } from '@/utils/formatTime'
// 表单校验
export const rules = reactive({
code: [required],
name: [required],
})
// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/
const crudSchemas = reactive<CrudSchema[]>([
{
label: 'id',
field: 'id',
isForm: false,
},
{
label: '编码',
field: 'code',
isSearch: true,
},
{
label: '设计搞名称',
field: 'name',
isSearch: true,
},
{
label: '作者',
field: 'author',
isSearch: true,
},
{
label: '版本',
field: 'version',
isSearch: false,
form: {
component: 'InputNumber',
componentProps:{
disabled: true
},
value: 0
},
},
{
label: '语言标识',
field: 'locale',
dictType: DICT_TYPE.LANGUAGE_LOCALE,
dictClass: 'string',
search: {
show: true,
},
form: {
component: 'SelectV2'
},
},
{
label: '启用状态',
field: 'enabled',
isSearch: false,
},
{
label: '备注',
field: 'remark',
isSearch: false,
},
{
label: '创建时间',
field: 'createTime',
formatter: dateFormatter,
isSearch: true,
search: {
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
type: 'daterange',
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
}
},
isForm: false,
}
])
export const { allSchemas } = useCrudSchemas(crudSchemas)

View File

@ -0,0 +1,294 @@
<template>
<slot>
<div>
<el-input
v-model="that.showValue"
clearable
:placeholder="props.placeholder"
@clear="clearData"
@click="viewDetails"
>
<template #append>
<el-button @click.stop="openDialog">
<Icon icon="ep:search"/>
</el-button>
</template>
</el-input>
</div>
</slot>
<Dialog
:title="dialogTitle"
width="80%"
v-model="that.visible"
@close="updateVisible(false)">
<div>
<!-- 搜索工作栏 -->
<ContentWrap>
<Search
:schema="allSchemas.searchSchema"
:is-col="false"
@search="setSearchParams"
@reset="setSearchParams">
<!-- 新增等操作按钮 -->
<template #actionMore>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['oms:draft-design-data:create']"
>
<Icon icon="ep:plus" class="mr-5px"/>
新增
</el-button>
</template>
</Search>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<Table
:columns="allSchemas.tableColumns"
:data="tableObject.tableList"
:loading="tableObject.loading"
:selection="true"
ref="tableRef"
@selection-change="selectionChange"
:pagination="{
total: tableObject.total
}"
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
>
<template #action="{ row }">
<el-button
link
type="primary"
@click="openForm('update', row.id)"
v-hasPermi="['oms:draft-design-data:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
v-hasPermi="['oms:draft-design-data:delete']"
@click="handleDelete(row.id)"
>
删除
</el-button>
</template>
</Table>
</ContentWrap>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="updateVisible(false,true)">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ t('common.ok') }}</el-button>
</span>
</template>
</Dialog>
<!-- 表单弹窗添加/修改 -->
<DataForm ref="formRef" @success="getList"/>
</template>
<script lang="ts" setup name="DraftDesignDataListDialog">
import {allSchemas} from './config.data'
import {DraftDesignDataApi} from "@/api/oms/draftdesigndata";
import DataForm from './DataForm.vue'
/** 稿件图片库 */
defineOptions({name: 'DraftDesignDataListDialog'})
const emit = defineEmits(['update:modelValue', 'update:visible', 'submit']) // success
const {t} = useI18n() //
const dialogTitle = ref('设计稿件') //
const props = defineProps({
modelValue: {
type: [String],
required: true
},
dataKey: {
type: String,
required: false,
default: 'id'
},
showKey: {
type: String,
required: false,
default: 'name'
},
multiple: {
type: Boolean,
required: false,
default: false
},
placeholder: {
type: String,
required: false,
default: '请选择'
},
width: {
type: String,
required: false,
default: '64px'
},
height: {
type: String,
required: false,
default: '64px'
},
visible: {
type: Boolean,
required: false,
default: false
}
})
const that = reactive({
inputVal: '',
showValue: '',
visible: false,
})
const openDialog = () => {
updateVisible(true);
}
const viewDetails = () => {
if (that.inputVal) {
openForm("preview", that.inputVal.split(",")[0])
} else {
openDialog();
}
console.log("viewDetails", viewDetails)
}
watch(() => props.modelValue, (newVal) => {
that.inputVal = toStr(newVal);
if (that.inputVal) {
initInput();
}
})
const toStr = (data: any) => {
if (data !== null && data !== undefined) {
return `${data}`
}
return data
}
let map = new Map();
const initInput = async () => {
const dataKey = that.inputVal + ',' + props.dataKey + ',' + props.showKey + ',' + props.multiple;
if (map.has(dataKey)) {
const data = map.get(dataKey)
if (data) {
that.inputVal = data.inputVal
that.showValue = data.showValue
console.log('缓存数据', data)
return;
}
}
const ids = that.inputVal.split(",");
let tmpInput = [];
let tmpShow = [];
for (let i = 0; i < ids.length; i++) {
const data = await DraftDesignDataApi.getDraftDesignData(ids[i])
tmpInput.push(data[props.dataKey]);
tmpShow.push(data[props.showKey]);
}
that.inputVal = tmpInput.join(',');
that.showValue = tmpShow.join(',');
map.set(dataKey, {
inputVal: that.inputVal,
showValue: that.showValue
})
}
watch(() => props.visible, (newVal) => {
that.visible = newVal;
})
const clearData = () => {
that.inputVal = '';
that.showValue = '';
updateValue();
}
const updateVisible = (visible: boolean, clearInput = false) => {
that.visible = visible;
emit("update:visible", visible)
if (clearInput) {
clearData();
}
}
defineExpose({}) // open
const submit = () => {
updateValue();
updateVisible(false)
}
const updateValue = () => {
emit("update:modelValue", that.inputVal)
}
//
const selectionChange = (row) => {
if (row && row.length > 0) {
if (props.multiple) {
that.inputVal = row.map(item => item[props.dataKey || 'id']).join(',')
that.showValue = row.map(item => item[props.showKey || 'id']).join(',')
} else {
if (row.length > 1) {
useMessage().warning('单选数据,已忽略其他')
}
that.showValue = row[row.length - 1][props.showKey || 'id']
that.inputVal = row[row.length - 1][props.dataKey || 'id']
}
} else {
that.inputVal = ''
that.showValue = ''
}
}
const {tableObject, tableMethods} = useTable({
getListApi: DraftDesignDataApi.getDraftDesignDataPage, //
delListApi: DraftDesignDataApi.deleteDraftDesignData //
})
//
const {getList, setSearchParams} = tableMethods
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = (id: number) => {
tableMethods.delList(id, false)
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
position: relative;
.el-input__inner {
padding-right: 18px;
}
.el-input__suffix {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
}
}
</style>

View File

@ -0,0 +1,248 @@
<template>
<slot>
<div>
<el-image
v-show="that.iconUrl"
@click="imagePreview(that.iconUrl)"
:style="{ width: props.width, height: props.height, }"
:src="that.iconUrl"/>
<el-input
v-model="that.iconUrl"
clearable
:placeholder="props.placeholder"
@clear="clearData"
@click="openDialog">
<template #append>
<el-button :icon="Search" @click="openDialog"/>
</template>
</el-input>
</div>
</slot>
<el-dialog
v-model="that.showImageLib"
append-to-body
:destroy-on-close="false"
:close-on-click-modal="false">
<div>
<ImageLibraryManage @close="uploadSuccess"/>
</div>
</el-dialog>
<Dialog :title="dialogTitle" v-model="that.visible" @close="updateVisible(false)">
<div>
<!-- 列表 -->
<ContentWrap>
<div
v-loading="that.imgLoading"
style="margin: 8px;padding-top: 12px">
<div style="display: flex">
<el-input clearable v-model="that.searchKeyword" placeholder="输入关键字搜索"/>
<el-button @click="queryImage">查询</el-button>
<el-button @click="that.showImageLib = true">图片上传</el-button>
</div>
<el-scrollbar height="500px">
<div style="display: flex;flex-wrap: wrap">
<el-radio-group v-model="that.iconUrl">
<div
v-for="(item) in that.imageUrlList"
:title="item.label"
:key="item.key">
<el-button link>
<div class="image-content-item">
<el-radio :value="item.icon"/>
<img :src="item.icon" width="60px" height="60px"/>
<div style="width: 84px;">
<span
class="line-clamp-1"
style="font-size: 0.8rem"
v-html="toHighlightText(item.label,that.searchKeyword)"></span>
</div>
</div>
</el-button>
</div>
</el-radio-group>
</div>
</el-scrollbar>
</div>
<!-- 分页 -->
<Pagination
:total="that.total"
v-model:page="that.queryParams.pageNo"
v-model:limit="that.queryParams.pageSize"
@pagination="queryImage"
/>
</ContentWrap>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="updateVisible(false)">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ t('common.ok') }}</el-button>
</span>
</template>
</Dialog>
</template>
<script lang="ts" setup name="DraftDesignImageLibDialog">
import {toHighlightText} from "@/components/DraftDesign/utils/FuncUtil";
import {ElButton, ElInput} from "element-plus";
import ImageLibraryManage from "@/components/DraftDesign/components/ImageLibraryManage.vue";
import {ShapeTemplateApi} from "@/api/oms/shapetemplate";
import * as FileApi from "@/api/infra/file";
import {replaceDomain} from "@/utils";
import {ShapeType} from "@/components/DraftDesign/config";
import {Search} from "@element-plus/icons-vue";
import {createImageViewer} from "@/components/ImageViewer";
/** 稿件图片库 */
defineOptions({name: 'DraftDesignImageLibDialog'})
const emit = defineEmits(['update:modelValue', 'update:visible', 'submit']) // success
const {t} = useI18n() //
const dialogTitle = ref('设计稿件图库') //
const props = defineProps({
modelValue: {
type: [String],
required: true
},
placeholder: {
type: String,
required: false,
default: '请选择'
},
width: {
type: String,
required: false,
default: '64px'
},
height: {
type: String,
required: false,
default: '64px'
},
visible: {
type: Boolean,
required: false,
default: false
}
})
const that = reactive({
imgLoading: false,
showImageLib: false,
searchKeyword: '',
iconUrl: '',
visible: false,
imageKeySet: [],
imageUrlList: [],
total: 0,
queryParams: {
pageNo: 1,
pageSize: 10,
shapeType: ShapeType.vueShapeImage,
name: "",
},
})
const uploadSuccess = () => {
that.showImageLib = false;
queryImage()
}
const openDialog = () => {
updateVisible(true);
queryImage();
}
const queryImage = async () => {
that.imgLoading = true
that.queryParams.name = that.searchKeyword;
const data = await ShapeTemplateApi.getShapeTemplatePage(that.queryParams)
const domain = await FileApi.getDomain();
that.total = data.total;
that.imageUrlList = [];
that.imageKeySet = [];
for (let i = 0; i < data.list.length; i++) {
let config = JSON.parse(data.list[i].initData)
config.filterKeyword = function () {
return this.label
}
config.key = `${config.key}_${data.list[i].id}`
//@ts-ignore
if (that.imageKeySet.includes(config.key)) {
continue;
}
//@ts-ignore
that.imageKeySet.push(config.key)
//
config.icon = replaceDomain(domain, config.icon)
config.data.style.shape.href = replaceDomain(domain, config.data.style.shape.href)
config.data.style.shape.label = config.label
//@ts-ignore
that.imageUrlList.push(config)
console.log("that.imageUrlList", that.imageUrlList)
}
that.imgLoading = false
// that.imageUrlList = data.list
}
watch(() => props.modelValue, (newVal) => {
that.iconUrl = newVal;
})
watch(() => props.visible, (newVal) => {
that.visible = newVal;
if (that.visible) {
queryImage()
}
})
const imagePreview = (imgUrl: string) => {
createImageViewer({
zIndex: 9999999,
urlList: [imgUrl]
})
}
const updateVisible = (visible: boolean) => {
that.visible = visible;
emit("update:visible", visible)
}
defineExpose({}) // open
const submit = () => {
emit("update:modelValue", that.iconUrl)
updateVisible(false)
}
const clearData = ()=>{
that.iconUrl = '';
emit("update:modelValue", that.iconUrl)
}
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
position: relative;
.el-input__inner {
padding-right: 18px;
}
.el-input__suffix {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
}
}
.image-content-item {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
margin: 8px;
border: 1px solid var(--el-border-color);
}
</style>

View File

@ -0,0 +1,70 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<Form :disabled="disabled" ref="formRef" :schema="allSchemas.formSchema" :rules="rules" v-loading="formLoading" />
<template #footer>
<el-button v-if="!disabled" @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import {ProductTypeApi,ProductTypeVO} from '@/api/base/producttype'
import { rules, allSchemas } from './config.data'
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formRef = ref() // Ref
const disabled = computed(() => {
return formType.value !== 'create' && formType.value !== 'update'
})
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
//
if (id) {
formLoading.value = true
try {
const data = await ProductTypeApi.getProductType(id)
formRef.value.setValues(data)
} finally {
formLoading.value = false
}
}
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
if (!formRef) return
const valid = await formRef.value.getElFormRef().validate()
if (!valid) return
//
formLoading.value = true
try {
const data = formRef.value.formModel as ProductTypeVO
if (formType.value === 'create') {
await ProductTypeApi.createProductType(data)
message.success(t('common.createSuccess'))
} else {
await ProductTypeApi.updateProductType(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
</script>

View File

@ -0,0 +1,89 @@
import type { CrudSchema } from '@/hooks/web/useCrudSchemas'
import { dateFormatter } from '@/utils/formatTime'
// 表单校验
export const rules = reactive({
value: [required],
label: [required],
})
// CrudSchema https://doc.iocoder.cn/vue3/crud-schema/
const crudSchemas = reactive<CrudSchema[]>([
{
label: 'id',
field: 'id',
isForm: false,
},
{
label: '编码',
field: 'value',
isSearch: true,
},
{
label: '名称',
field: 'label',
isSearch: true,
},
{
label: '排序号',
field: 'sort',
isSearch: true,
form: {
component: 'InputNumber',
value: 0
},
},
{
label: '语言标识',
field: 'locale',
dictType: DICT_TYPE.LANGUAGE_LOCALE,
dictClass: 'string',
search: {
show: true,
},
form: {
component: 'SelectV2'
},
},
{
label: '扩展项',
field: 'extendInfo',
isTable: false,
form: {
component: 'Input',
componentProps: {
type: 'textarea',
rows: 4
},
colProps: {
span: 24
}
},
},
{
label: '备注',
field: 'remark',
isTable: false,
},
{
label: '创建时间',
field: 'createTime',
formatter: dateFormatter,
isSearch: true,
search: {
component: 'DatePicker',
componentProps: {
valueFormat: 'YYYY-MM-DD HH:mm:ss',
type: 'daterange',
defaultTime: [new Date('1 00:00:00'), new Date('1 23:59:59')]
}
},
isForm: false,
},
{
label: '操作',
field: 'action',
isForm: false
}
])
export const { allSchemas } = useCrudSchemas(crudSchemas)

View File

@ -0,0 +1,294 @@
<template>
<slot>
<div>
<el-input
v-model="that.showValue"
clearable
:placeholder="props.placeholder"
@clear="clearData"
@click="viewDetails"
>
<template #append>
<el-button @click.stop="openDialog">
<Icon icon="ep:search"/>
</el-button>
</template>
</el-input>
</div>
</slot>
<Dialog
:title="dialogTitle"
width="80%"
v-model="that.visible"
@close="updateVisible(false)">
<div>
<!-- 搜索工作栏 -->
<ContentWrap>
<Search
:schema="allSchemas.searchSchema"
:is-col="false"
@search="setSearchParams"
@reset="setSearchParams">
<!-- 新增等操作按钮 -->
<template #actionMore>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['oms:draft-design-data:create']"
>
<Icon icon="ep:plus" class="mr-5px"/>
新增
</el-button>
</template>
</Search>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<Table
:columns="allSchemas.tableColumns"
:data="tableObject.tableList"
:loading="tableObject.loading"
:selection="true"
ref="tableRef"
@selection-change="selectionChange"
:pagination="{
total: tableObject.total
}"
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
>
<template #action="{ row }">
<el-button
link
type="primary"
@click="openForm('update', row.id)"
v-hasPermi="['oms:draft-design-data:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
v-hasPermi="['oms:draft-design-data:delete']"
@click="handleDelete(row.id)"
>
删除
</el-button>
</template>
</Table>
</ContentWrap>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="updateVisible(false,true)">{{ t('common.cancel') }}</el-button>
<el-button type="primary" @click="submit">{{ t('common.ok') }}</el-button>
</span>
</template>
</Dialog>
<!-- 表单弹窗添加/修改 -->
<DataForm ref="formRef" @success="getList"/>
</template>
<script lang="ts" setup name="ProductTypeDataListDialog">
import {allSchemas} from './config.data'
import {ProductTypeApi} from '@/api/base/producttype'
import DataForm from './DataForm.vue'
/** 稿件图片库 */
defineOptions({name: 'ProductTypeDataListDialog'})
const emit = defineEmits(['update:modelValue', 'update:visible', 'submit']) // success
const {t} = useI18n() //
const dialogTitle = ref('设计稿件') //
const props = defineProps({
modelValue: {
type: [String],
required: true
},
dataKey: {
type: String,
required: false,
default: 'id'
},
showKey: {
type: String,
required: false,
default: 'label'
},
multiple: {
type: Boolean,
required: false,
default: false
},
placeholder: {
type: String,
required: false,
default: '请选择'
},
width: {
type: String,
required: false,
default: '64px'
},
height: {
type: String,
required: false,
default: '64px'
},
visible: {
type: Boolean,
required: false,
default: false
}
})
const that = reactive({
inputVal: '',
showValue: '',
visible: false,
})
const openDialog = () => {
updateVisible(true);
}
const viewDetails = () => {
if (that.inputVal) {
openForm("preview", that.inputVal.split(",")[0])
} else {
openDialog();
}
console.log("viewDetails", viewDetails)
}
watch(() => props.modelValue, (newVal) => {
that.inputVal = toStr(newVal);
if (that.inputVal) {
initInput();
}
})
const toStr = (data: any) => {
if (data !== null && data !== undefined) {
return `${data}`
}
return data
}
let map = new Map();
const initInput = async () => {
const dataKey = that.inputVal + ',' + props.dataKey + ',' + props.showKey + ',' + props.multiple;
if (map.has(dataKey)) {
const data = map.get(dataKey)
if (data) {
that.inputVal = data.inputVal
that.showValue = data.showValue
console.log('缓存数据', data)
return;
}
}
const ids = that.inputVal.split(",");
let tmpInput = [];
let tmpShow = [];
for (let i = 0; i < ids.length; i++) {
const data = await ProductTypeApi.getProductType(ids[i])
tmpInput.push(data[props.dataKey]);
tmpShow.push(data[props.showKey]);
}
that.inputVal = tmpInput.join(',');
that.showValue = tmpShow.join(',');
map.set(dataKey, {
inputVal: that.inputVal,
showValue: that.showValue
})
}
watch(() => props.visible, (newVal) => {
that.visible = newVal;
})
const clearData = () => {
that.inputVal = '';
that.showValue = '';
updateValue();
}
const updateVisible = (visible: boolean, clearInput = false) => {
that.visible = visible;
emit("update:visible", visible)
if (clearInput) {
clearData();
}
}
defineExpose({}) // open
const submit = () => {
updateValue();
updateVisible(false)
}
const updateValue = () => {
emit("update:modelValue", that.inputVal)
}
//
const selectionChange = (row) => {
if (row && row.length > 0) {
if (props.multiple) {
that.inputVal = row.map(item => item[props.dataKey || 'id']).join(',')
that.showValue = row.map(item => item[props.showKey || 'id']).join(',')
} else {
if (row.length > 1) {
useMessage().warning('单选数据,已忽略其他')
}
that.showValue = row[row.length - 1][props.showKey || 'id']
that.inputVal = row[row.length - 1][props.dataKey || 'id']
}
} else {
that.inputVal = ''
that.showValue = ''
}
}
const {tableObject, tableMethods} = useTable({
getListApi: ProductTypeApi.getProductTypePage, //
delListApi: ProductTypeApi.deleteProductType //
})
//
const {getList, setSearchParams} = tableMethods
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = (id: number) => {
tableMethods.delList(id, false)
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
position: relative;
.el-input__inner {
padding-right: 18px;
}
.el-input__suffix {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
}
}
</style>

View File

@ -0,0 +1,65 @@
<template>
<Dialog
v-model="that.previewVisible"
title="预览"
width="80vw"
top="20px"
append-to-body
@close="updateVisible(false)">
<div v-loading="that.loading">
<DesignPropEdit ref="designPropEditRef" />
</div>
</Dialog>
</template>
<script lang="ts" setup name="DesignPreviewDialog">
import {ElAlert} from "element-plus";
import {reactive, computed, watch} from 'vue'
import {calculateVectorDifference} from "@/components/DraftDesign/utils/FuncUtil";
import {useMessage} from "@/hooks/web/useMessage";
const emit = defineEmits(['update:visible', 'submit'])
const designPropEditRef = ref();
const that = reactive({
previewVisible: false,
loading: false,
})
const updateVisible = (visible)=>{
that.previewVisible = visible;
}
const preview = (config,prop={})=>{
updateVisible(true);
that.loading = true;
setTimeout(()=>{
designPropEditRef.value.loadConfig(config,prop);
that.loading = false;
},300)
}
// id
const previewByProductId = (id: string | number,prop={})=>{
updateVisible(true);
that.loading = true;
setTimeout(()=>{
designPropEditRef.value.previewByProductId(id,prop);
that.loading = false;
},300)
}
// 稿id
const previewByDraftDesignId = (id: string | number,prop={})=>{
updateVisible(true);
that.loading = true;
setTimeout(()=>{
designPropEditRef.value.previewByDraftDesignId(config,prop);
that.loading = false;
},300)
}
defineExpose({
preview,
previewByProductId,
previewByDraftDesignId,
updateVisible,
})
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,475 @@
<!-- eslint-disable vue/this-in-template -->
<template>
<div style="max-height: 90vh; overflow:auto;">
<span>
仅供参考(for reference only),绿色边框属于边界
</span>
<div style="padding-bottom: 4px">
<el-button @click="that.currentZoom -= 1" :disabled="that.currentZoom <= 1">-
</el-button>
<el-select v-model="that.currentZoom" style="width: 90px">
<el-option
v-for="item in zoomList"
:key="item.value"
:label="(Math.floor(item.value))"
:value="item.value"
/>
<el-option
v-if="!zoomList.some(o => o.value === that.currentZoom)"
:label="(Math.floor(that.currentZoom))"
:value="that.currentZoom"
/>
</el-select>
<el-button @click="that.currentZoom += 1" :disabled="that.currentZoom >= 30">+
</el-button>
</div>
<div style="display: flex; flex-wrap: wrap;">
<div v-if="that.previewUrl" style="margin: 2px; box-shadow: 0 0 1px 1px rgb(0,255,0)">
<el-image
@click="imagePreview(that.previewUrl)"
v-loading="loading"
:src="that.previewUrl"/>
</div>
<div v-if="that.propOrderByList && that.propOrderByList.length > 0">
<el-scrollbar height="600px">
<el-form label-width="280px">
<el-form-item
v-for="(tmp) in that.propOrderByList"
:key="tmp.key"
:label="getLabelName(that.propInfo[tmp.key])">
<div v-if="that.propInfo[tmp.key].shape === ShapeType.vueTextCell">
<div
v-if="that.propInfo[tmp.key].isCombo && that.propInfo[tmp.key].dataInfo.length === 2">
<!-- 图片文本 组合-->
<div style="display: flex">
<div style="display: flex;align-items: center;">
<div>
<i
v-if="that.propInfo[tmp.key].canInput"
class="icon-lk_edit"
style="font-size: 20px"> </i>
<i v-else class="icon-lk_cell_add" style="font-size: 20px"> </i>
</div>
<el-select-v2
v-model="that.propInfo[tmp.key].dataInfo[0].label"
filterable
:options="that.washingInfoList"
placeholder="Please select"
@change="changeComboData(that.propInfo[tmp.key],that.propInfo[tmp.key].dataInfo[0].label)"
size="large"
style="min-width: 280px;width: 100%"
>
<template #default="{ item }">
<div style="display: flex;">
<img :src="item.url" width="30px" height="30px"/>
<span>
{{ item.label }}
</span>
</div>
</template>
</el-select-v2>
</div>
<div class="img-box">
<div>
<img
:src="that.propInfo[tmp.key].dataInfo[1].url"
width="60px"
height="60px"/>
</div>
</div>
<div>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(info,-1)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
</div>
<div
v-else
v-for="(text,index) in that.propInfo[tmp.key].dataInfo"
:key="index"
style="padding: 4px">
<div style="display: flex;align-items: center;">
<span>
<i
v-if="that.propInfo[tmp.key].canInput"
class="icon-lk_edit"
style="font-size: 20px"> </i>
<i v-else class="icon-lk_cell_add" style="font-size: 20px"> </i>
</span>
<el-autocomplete
v-if="that.propInfo[tmp.key].canInput"
v-model="that.propInfo[tmp.key].dataInfo[index].label"
:fetch-suggestions="querySearch"
clearable
class="inline-input w-50"
placeholder="Please Input"
@change="changeData"
@select="changeData"
/>
<el-select-v2
v-else
v-model="that.propInfo[tmp.key].dataInfo[index].label"
filterable
:options="that.ingredientInfoList"
placeholder="Please select"
@change="changeData"
size="large"
style="min-width: 280px;width: 100%"
/>
<el-button
v-if="that.propInfo[tmp.key].canChange"
@click="deleteList(info,index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
</div>
<el-button v-if="that.propInfo[tmp.key].canChange" @click="appendList(info)">
添加
</el-button>
</div>
<div v-else-if="that.propInfo[tmp.key].shape === ShapeType.vueShapeImage">
<div
v-for="(img,index) in that.propInfo[tmp.key].dataInfo"
:key="index" style="display: flex">
<div class="img-box">
<div>
<img :src="img.url" width="60px" height="60px"/>
</div>
</div>
<el-button>
<i class="icon-lk_image"></i>
</el-button>
<el-button @click="deleteList(info,index)">
<i class="icon-lk_delete"> </i>
</el-button>
</div>
<el-button @click="appendList(info)">
添加
</el-button>
</div>
</el-form-item>
</el-form>
</el-scrollbar>
</div>
</div>
<div
:class="{ 'hidden-div': true }">
<DraftDesign ref="draftDesignEditRef"/>
</div>
</div>
</template>
<script lang="ts" setup name="DynamicPropConfig">
import {ElButton, ElSelect, ElSelectV2} from "element-plus";
import {reactive, watch} from 'vue'
import {useMessage} from "@/hooks/web/useMessage";
import {DraftDesignDataApi} from "@/api/oms/draftdesigndata";
import DraftDesign from "@/components/DraftDesign/index.vue";
import {ShapeType} from "@/components/DraftDesign/config";
import {createImageViewer} from "@/components/ImageViewer";
import {ProductCareItemApi} from "@/api/oms/productcareitem";
import {replaceDomain} from "@/utils";
import * as FileApi from "@/api/infra/file";
import {useLocaleStore} from "@/store/modules/locale";
import {ProductInfoApi} from "@/api/oms/productinfo";
//
const localeStore = useLocaleStore()
const emit = defineEmits(['change'])
const draftDesignEditRef = ref()
const that = reactive({
propInfo: {
groupName: '', //
isCombo: false, //
minSize: 1,
maxSize: 100,
canChange: false,
canInput: false,
cellIds: [], // id
groupId: `g_${Math.random().toString(36).substring(2)}`,
dataInfo: [], //
shape: '', //
},
propOrderByList: [],
currentZoom: 6,
previewUrl: "",
ingredientInfoList: [],
washingInfoList: [],
pageConfig: {
background: null,
},
data: {},
show: false,
})
const zoomList = [
{
value: 1,
label: '1'
},
{
value: 5,
label: '5'
},
{
value: 6,
label: '6'
},
{
value: 10,
label: '10'
}, {
value: 15,
label: '15'
}, {
value: 20,
label: '20'
}, {
value: 30,
label: '30'
},
]
const currentLocale = computed(() => localeStore.getCurrentLocale)
watch(() => that.currentZoom, () => {
showPng();
})
const querySearch = (queryString: string, cb: any) => {
const results = queryString ? that.ingredientInfoList.filter((item: any) => {
return item.value.toLowerCase().indexOf(queryString.toLowerCase()) !== -1
}) : that.ingredientInfoList
cb(results)
}
const imagePreview = (imgUrl: string) => {
createImageViewer({
zIndex: 9999999,
urlList: [imgUrl]
})
}
const getLabelName = (item) => {
if (item.shape === ShapeType.vueShapeImage) {
return item.groupName + "(icon):"
}
return item.groupName + ":"
}
const loading = ref(false);
const changeData = () => {
console.log("that.propInfo", that.propInfo)
resetData();
}
const changeComboData = (item: any, label: string) => {
for (let i = 0; i < that.washingInfoList.length; i++) {
// @ts-ignore
if (that.washingInfoList[i].value === label) {
// @ts-ignore
item.dataInfo[1].url = that.washingInfoList[i].url
break;
}
}
resetData();
}
const deleteList = (item, index) => {
item.dataInfo.splice(index, 1)
resetData();
}
const appendList = (item) => {
item.dataInfo.push(
{
label: '',
url: '',
...item.dataInfo[item.dataInfo.length - 1]
}
)
resetData();
}
const formData = ref({
id: undefined,
code: undefined,
name: undefined,
author: undefined,
version: 1,
locale: 'zh-CN',
enabled: undefined,
remark: undefined,
details: undefined,
propDefault: undefined,
})
// 稿id
const previewByDraftDesignId = async (id: string | number, propData = {}) => {
if (id) {
const res = await DraftDesignDataApi.getDraftDesignData(id)
if (res) {
formData.value = res
const j = formData.value.details || "{}"
const d = JSON.parse(j)
loadConfig(d, propData);
} else {
useMessage().error('获取数据不存在:' + id)
}
}
}
// id
const previewByProductId = async (id: string | number, propData = {}) => {
if (id) {
const prodRes = await ProductInfoApi.getProductInfo(id)
if (!prodRes.draftDesignDataId) {
useMessage().error('商品未关联设计图稿数据:' + id)
return;
}
const res = await DraftDesignDataApi.getDraftDesignData(prodRes.draftDesignDataId)
if (res) {
formData.value = res
const j = formData.value.details || "{}"
const d = JSON.parse(j)
loadConfig(d, propData);
} else {
useMessage().error('获取数据不存在:' + id)
}
}
}
const loadConfig = (config: object, propData = {}) => {
console.log("config", config)
that.pageConfig = config.pageConfig
that.data = config.data
that.propInfo = propData
// true, conf.pageConfig, conf.data, test
draftDesignEditRef.value.init(false, that.pageConfig, that.data, that.propInfo)
showPng();
queryUseLabel();
}
const queryUseLabel = (label) => {
FileApi.getDomain().then(domain => {
ProductCareItemApi.getProductCareItemPage({
pageNo: 1,
pageSize: 100,
value: label,
locale: currentLocale.value.lang,
isCombo: false,
}).then((data) => {
that.ingredientInfoList = [];
if (data.list) {
that.ingredientInfoList = data.list.map((item) => {
return {
value: item.value,
label: item.value,
url: ''
}
})
}
})
ProductCareItemApi.getProductCareItemPage({
pageNo: 1,
pageSize: 100,
value: label,
locale: currentLocale.value.lang,
isCombo: true,
}).then((data) => {
that.washingInfoList = [];
if (data.list) {
that.washingInfoList = data.list.map((item) => {
return {
value: item.value,
label: item.value,
url: replaceDomain(domain, item.iconUrl)
}
})
}
})
});
}
const showPng = () => {
if (that.pageConfig && that.pageConfig.background) {
loading.value = true
setTimeout(() => {
draftDesignEditRef.value.toPngUrl((url) => {
that.previewUrl = url
const info = draftDesignEditRef.value.getPropInfo();
console.log("info", info)
that.propInfo = info.propDefault;
that.propOrderByList = info.propOrderByList;
submit();
loading.value = false;
}, {
widthScale: that.currentZoom,
heightScale: that.currentZoom
})
}, 200)
}
}
const resetData = () => {
setTimeout(() => {
draftDesignEditRef.value.init(false, that.pageConfig, that.data, that.propInfo)
showPng();
}, 100)
}
const getPropInfo = () => {
return that.propInfo;
}
const submit = () => {
const info = getPropInfo();
emit('change', info)
}
onMounted(() => {
})
defineExpose({
previewByDraftDesignId,
previewByProductId,
loadConfig,
getPropInfo
})
</script>
<style lang="scss" scoped>
:deep(.el-input__wrapper) {
position: relative;
.el-input__inner {
padding-right: 18px;
}
.el-input__suffix {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
}
}
.hidden-div {
position: fixed;
left: -100000px;
}
.img-box {
border: 1px solid rgba(182, 181, 181, 0.7);
border-radius: 10px;
margin: 8px 2px;
div {
margin: 4px;
width: 60px;
height: 60px;
}
}
</style>

View File

@ -2,10 +2,11 @@
<el-dialog
v-model="that.show"
title="动态属性配置"
width="80vw"
width="60vw"
draggable
append-to-body
@close="updateVisible(false)">
<div>
<div style="width: 100%">
<el-alert
title="动态属性配置说明"
type="info"
@ -16,7 +17,7 @@
"位置信息配置为节点的位置信息。如添加了3个配置数据这时会依次添加到对应位置上。从而实现动态属性配置。"
</el-alert>
<el-form>
<el-row >
<el-row>
<el-col :span="6" :xs="12">
<el-form-item label="组名称">
<el-input v-model="that.configInfo.groupName" placeholder="请输入组名称"/>
@ -28,28 +29,82 @@
</el-form-item>
</el-col>
</el-row>
<el-form-item label="控制">
<el-row>
<el-col :span="12">
<div style="width: 120px;display: flex">
<el-checkbox
v-model="that.configInfo.canChange"
:disabled="that.configInfo.isCombo">
<span>允许调整数量</span>
</el-checkbox>
</div>
</el-col>
<el-col :span="12">
<div style="width: 120px;display: flex">
<el-checkbox
v-model="that.configInfo.canInput"
:disabled="that.configInfo.isCombo">
<span>允许输入值</span>
</el-checkbox>
</div>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="数量">
<el-row>
<el-col :span="11">
min
<el-input-number
v-model="that.configInfo.minSize"
:min="1"
:max="that.configInfo.maxSize"
:disabled="that.configInfo.isCombo"/>
</el-col>
<el-col :span="11">
max
<el-input-number
v-model="that.configInfo.maxSize" :min="1"
:disabled="that.configInfo.isCombo"/>
</el-col>
</el-row>
</el-form-item>
<el-form-item label="位置信息">
<div style="display: flex;flex-direction: column">
<div>
<div v-if="!that.configInfo.isCombo && that.configInfo.pointList.length > 1">
<el-button @click="append(1)">追加</el-button>
<el-button @click="append(10)">追加10个</el-button>
<el-button type="danger" @click="delLast">删除</el-button>
</div>
<div v-else-if="!that.configInfo.isCombo">
<span style="font-size: 0.8rem;color: #7c7b7b">两个或者以上允许追加更多位置信息</span>
</div>
<el-scrollbar height="400px">
<div style=" box-shadow: 2px 0 0 1px #eee;" v-for="(item, index) in that.configInfo.pointList" :key="index">
<div
style=" box-shadow: 2px 0 0 1px #eee;"
v-for="(item, index) in that.configInfo.pointList"
:key="index">
<el-row>
<el-col :span="2">{{index +1}}</el-col>
<el-col :span="2">{{ index + 1 }}</el-col>
<el-col :span="11">
X向右
<span><span v-if="that.configInfo.isCombo" style="padding-right: 4px">
{{ index === 0 ? 'text' : 'icon' }}
</span> X向右</span>
<el-input-number v-model="that.configInfo.pointList[index].x"/>
</el-col>
<el-col :span="11">
Y向下
<span>
<span
v-if="that.configInfo.isCombo"
style="padding-right: 4px">
{{ index === 0 ? 'text' : 'icon' }}
</span>
Y向下</span>
<el-input-number v-model="that.configInfo.pointList[index].y"/>
</el-col>
<hr/>
</el-row>
</div>
</el-scrollbar>
</div>
@ -72,13 +127,18 @@ import {calculateVectorDifference} from "@/components/DraftDesign/utils/FuncUtil
import {useMessage} from "@/hooks/web/useMessage";
//
const emit = defineEmits(['update:visible','submit'])
const emit = defineEmits(['update:visible', 'submit'])
const that = reactive({
allGroupList: [],
show: false,
configInfo:{
configInfo: {
groupName: '', //
isCombo: false, //
minSize: 1,
maxSize: 100,
canChange: false,
canInput: false,
cellIds: [], // id
groupId: `g_${Math.random().toString(36).substring(2)}`,
data: {}, //
@ -93,7 +153,7 @@ const props = defineProps({
}
})
const delLast = () => {
if(that.configInfo.pointList.length === 1){
if (that.configInfo.pointList.length === 1) {
useMessage().warning(`至少需要1个位置信息`)
return;
}
@ -101,11 +161,12 @@ const delLast = () => {
useMessage().success(`成功删除1个位置信息`)
}
const appendNode = () => {
const arr = that.configInfo.pointList
const tmp = calculateVectorDifference(arr[arr.length - 1],arr[arr.length - 2])
const arr = that.configInfo.pointList
const tmp = calculateVectorDifference(arr[arr.length - 1], arr[arr.length - 2])
const dx = arr[arr.length - 1].x + tmp.x
const dy = arr[arr.length - 1].y + tmp.y
//@ts-ignore
const dx = arr[arr.length - 1].x + tmp.x, dy = arr[arr.length - 1].y + tmp.y;
//@ts-ignore
that.configInfo.pointList.push({
x: dx,
y: dy,
@ -113,19 +174,25 @@ const appendNode = () => {
size: {...arr[arr.length - 1].size}
})
}
const append = (count = 1)=>{
const append = (count = 1) => {
let i = 0;
while (i < count){
appendNode()
i++;
}
while (i < count) {
appendNode()
i++;
}
useMessage().success(`成功添加${count}个位置信息`)
}
const init = (allGroupList, data) => {
that.allGroupList = allGroupList;
that.configInfo = {
groupName: '', //
groupId: `g_${Math.random().toString(36).substring(2)}`,
isCombo: false, //
minSize: 1, //
maxSize: 100, //
canChange: false, //
canInput: false,//
cellIds: [], // id
data: {}, //
shape: '', //

View File

@ -88,6 +88,8 @@ const props = defineProps({
tipsText: propTypes.string.def('点击选择图片上传'),
})
const emit = defineEmits( ["close",'update:modelValue'])
const { uploadUrl, httpRequest } = useUpload()
const fileList = ref<UploadUserFile[]>([])
@ -116,20 +118,14 @@ const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
return imgType.includes(rawFile.type as FileTypes) && imgSize
}
const emit = defineEmits( ["close",'update:modelValue'])
const formData = ref({
id: undefined,
shapeType: undefined,
name: undefined,
initData: undefined,
})
const uploadSuccess: UploadProps['onSuccess'] = (res: any): void => {
message.success('上传成功')
//
//@ts-ignore
const index = fileList.value.findIndex((item) => item.response?.data === res.data)
fileList.value.splice(index, 1)
uploadList.value.push({
// @ts-ignore
filename: res.filename || res.data ,
name : res.data,
url: res.data })
@ -181,15 +177,18 @@ const addNew = async () => {
}
},
icon: fileList.value[i].url,
//@ts-ignore
label: fileList.value[i].filename || '未命名图片',
filterKeyword: function (){ return this.label }
};
const data = {
id: undefined,
shapeType: ShapeType.vueShapeImage,
//@ts-ignore
name: fileList.value[i].filename,
initData: JSON.stringify(info),
}
//@ts-ignore
await ShapeTemplateApi.createShapeTemplate(data)
}

View File

@ -16,8 +16,12 @@
v-loading="that.imgLoading"
style="margin: 8px;padding-top: 12px"
v-if="`shape_image` === layout.leftActive">
<el-input v-model="layout.searchKeyword" placeholder="输入关键字搜索"/>
<div style="display: flex">
<el-input clearable v-model="layout.searchKeyword" placeholder="输入关键字搜索"/>
<el-button @click="queryImage">查询</el-button>
</div>
<el-button @click="that.showImageLib = true">图片上传</el-button>
<el-scrollbar height="500px">
<div style="display: flex;flex-wrap: wrap">
<div
:title="item.label" class="image-item"
@ -37,6 +41,15 @@
</div>
</div>
</div>
</el-scrollbar>
<!-- 分页 -->
<Pagination
:total="that.total"
v-model:page="that.queryParams.pageNo"
v-model:limit="that.queryParams.pageSize"
@pagination="queryImage"
/>
</div>
<div style="margin: 8px;padding-top: 12px" v-else>
@ -153,11 +166,12 @@ const hasKey = (obj, path) => {
const that = reactive({
effect: 'dark',
showImageLib: false,
total:0,
queryParams: {
pageNo: 1,
pageSize: 80,
pageSize: 20,
shapeType: ShapeType.vueShapeImage,
name: undefined,
name: '',
},
imgLoading: false,
imageKeySet: [],
@ -204,9 +218,12 @@ const uploadSuccess = () => {
const queryImage = async () => {
that.imgLoading = true
that.queryParams.name = layout.searchKeyword;
const data = await ShapeTemplateApi.getShapeTemplatePage(that.queryParams)
const domain = await FileApi.getDomain();
that.total = data.total;
that.imageUrlList = [];
that.imageKeySet = [];
for (let i = 0; i < data.list.length; i++) {
let config = JSON.parse(data.list[i].initData)
config.filterKeyword = function () {
@ -293,8 +310,8 @@ const layout = reactive({
shape: ShapeType.vueShapeRect,
data: {
label: '',
width: 80,
height: 80,
width: 10,
height: 10,
shape: VueCellShapeType.Rect,
style: {
fontSize: 12,
@ -314,8 +331,8 @@ const layout = reactive({
shape: ShapeType.vueShapeRect,
data: {
label: '',
width: 80,
height: 80,
width: 10,
height: 10,
shape: VueCellShapeType.Rect,
style: {
fontSize: 12,
@ -336,8 +353,8 @@ const layout = reactive({
shape: ShapeType.vueShapeRect,
data: {
label: '',
width: 80,
height: 80,
width: 10,
height: 10,
shape: VueCellShapeType.Rect,
style: {
fontSize: 12,
@ -383,7 +400,7 @@ $height: 600px;
$mainPanelWidth: 368px;
$mainPanelToggleLeft: 367px;
$mainPanelLeft: 67px;
$panelZIndex: 10;
$panelZIndex: 20;
.mainPanel {
@ -411,6 +428,7 @@ $panelZIndex: 10;
}
.mainPanel-mainContent-toggle {
width: 10px;
height: 88px;
position: absolute;

View File

@ -183,6 +183,7 @@ defineExpose({
$height: 600px;
$mainPanelWidth: 260px;
.mainPanel {
z-index: 20;
background-color: #ffffff;
width: $mainPanelWidth;
height: $height;

View File

@ -4,7 +4,8 @@
<el-dialog
v-model="that.showDialog"
:close-on-click-modal="false"
draggable
:close-on-click-modal="true"
>
<template #title>
<div>
@ -17,8 +18,8 @@
<div>
<el-form-item label="backgroundColor">
<ColorPickerTool
v-model="that.pageConfig.background.color"
:uuid="`background.color`"
v-model="that.pageConfig.background.areaColor"
:uuid="`background.areaColor`"
@active-change="changePageColor"
show-alpha
:predefine="predefineColors"/>
@ -38,6 +39,14 @@
v-model="that.pageConfig.editArea.height"
@change="changePageConfig"/>
</div>
<div>
onLoadZoom
<el-input-number
:min="0.1"
:max="10"
v-model="that.pageConfig.onLoadZoom"
@change="changePageConfig"/>
</div>
</div>
<div>
<el-select
@ -69,7 +78,49 @@
</el-form>
</template>
</el-dialog>
<el-container>
<el-dialog
v-model="that.showOrderByDialog"
draggable
width="50vw"
:close-on-click-modal="true"
>
<template #title>
<div>
动态属性排序
</div>
</template>
<template #default>
<div>
<span style="font-size: 0.8rem;color: #b5b3b3">将关键必要属性进行排序让布局更清晰</span>
<span style="font-size: 0.8rem;color: #b5b3b3">按下鼠标左键进行拖拽进行排序拖拽结束后即可生效</span>
<Draggable
style="display: flex; flex-direction: column; align-items: center;"
ghost-class="draggable-ghost"
item-key="key"
@change="propListChange"
handler=".order-by-item"
:list="that.propList"
:group="{ name: 'orderItem'}"
:animation="200"
>
<template #item="{ element,index }">
<div class="order-by-item">
<span style="padding: 4px">{{index+1}}</span>
<i v-if="element.shape === ShapeType.vueShapeImage" class="icon-lk_image" title="icon属性"> </i>
<i v-else-if="element.isCombo" class="icon-lk_cell_add" title="组合属性"> </i>
<i v-else class="icon-lk_text"> </i>
<span style="padding: 4px;">{{element.name}}</span>
</div>
</template>
</Draggable>
</div>
</template>
</el-dialog>
<el-container >
<el-header class="top-tool-bar" v-show="layout.draftDesignState">
<div style="display: flex;align-items: center;height: 100%;">
<div style="background-color: #34bfa3;width: 68px;height: 100%;">
@ -77,9 +128,13 @@
<div
style="margin-left: 6px;display: flex;flex-wrap: wrap;align-items: center;height: 100%;max-width: 900px">
<div>
<el-button @click="that.showDialog = true">Settings</el-button>
<el-button @click="that.showDialog = true" :title="Setting">Settings</el-button>
</div>
<div>
<el-button @click="that.showOrderByDialog= true" :title="'属性排序'">
<i class="icon-lk_order_by"> </i>
</el-button>
</div>
<div v-if="hasKey(that.cellInfo, 'angle')">
<el-popover
placement="bottom-start"
@ -183,6 +238,7 @@
<div>
<el-button
title="保存Save(Ctrl+S)"
@click="btnChange('canSave',!that.btnState.canUndo)"
:disabled="!that.btnState.canSave">
<i class="icon-lk_save"></i>
@ -190,6 +246,7 @@
</div>
<div>
<el-button
title="清空Clear"
@click="btnChange('clearData',true)"
>
<i class="icon-lk_delete"></i>
@ -197,6 +254,7 @@
</div>
<div>
<el-button
title="撤销Undo(Ctrl+Z)"
@click="btnChange('canUndo',!that.btnState.canUndo)"
:disabled="!that.btnState.canUndo">
<i class="icon-lk_undo"></i>
@ -204,6 +262,7 @@
</div>
<div>
<el-button
title="重做Redo(Ctrl+Y)"
@click="btnChange('canRedo',!that.btnState.canRedo)"
:disabled="!that.btnState.canRedo">
<i class="icon-lk_recover"></i>
@ -597,7 +656,7 @@
</div>
<el-container>
<el-main class="center-panel">
<el-main class="center-panel" >
<div>
<slot>
<div>main</div>
@ -614,7 +673,7 @@
</div>
<div>
<div>
<el-button @click="that.currentZoom -= 0.1" :disabled="that.currentZoom <= 0.3">-
<el-button @click="that.currentZoom -= 0.1" :disabled="that.currentZoom <= 0.5">-
</el-button>
<el-select v-model="that.currentZoom" style="width: 90px">
<el-option
@ -645,6 +704,7 @@
</template>
<script setup lang="ts" name="DraftDesignLayout">
// @ts-nocheck
//稿 DraftDesign
import {
ElTabs,
@ -658,37 +718,22 @@ import {
ElSelectV2
} from "element-plus";
import {Graph, DataUri} from '@antv/x6'
import {Scroller} from '@antv/x6-plugin-scroller'
import {Snapline} from '@antv/x6-plugin-snapline'
import {Transform} from '@antv/x6-plugin-transform'
import {Keyboard} from '@antv/x6-plugin-keyboard'
import {History} from '@antv/x6-plugin-history'
import {Selection} from '@antv/x6-plugin-selection'
import {Dnd} from '@antv/x6-plugin-dnd'
import {Export} from '@antv/x6-plugin-export'
import {Clipboard} from '@antv/x6-plugin-clipboard'
import DraftDesignLibrary from './components/Library.vue'
import NodePropConfig from './components/NodePropConfig.vue'
import {provide, computed, watch, onMounted} from 'vue'
import {nextId, getSafeValue, hexToRGBA} from "@/components/DraftDesign/utils/FuncUtil";
import { computed, watch, onMounted} from 'vue'
import {nextId} from "@/components/DraftDesign/utils/FuncUtil";
import {useDebounceFn} from '@vueuse/core'
import {ElMessageBox} from 'element-plus'
import {register, getTeleport} from '@antv/x6-vue-shape'
import ProgressNode from './components/node/ProgressNode.vue'
import Draggable from 'vuedraggable'
import '../../icons/style.css'
import {getDraftDesignState, predefineColors, ShapeType} from "@/components/DraftDesign/config";
import ColorPickerTool from "@/components/DraftDesign/components/ColorPickerTool.vue";
import DraftDesignLeftPanel from "./LeftPanel.vue";
import DraftDesignRightPanel from "./RightPanel.vue";
const emit = defineEmits(['onAddNode', 'changeZoom', 'onChangePageConfig', 'onChangeCell', 'btnChange','selectCell'])
const props = defineProps({})
const clickMenu = (item) => {
layout.leftActive = item.key
console.log(item)
}
const emit = defineEmits(['addNode',
'changeZoom', 'changePageConfig',
'changeCell',
'btnChange',
'propListChange',
'selectCell'])
const buttonRef = ref()
const sizeRef = ref()
@ -703,9 +748,8 @@ const onClickOutSize = () => {
}
const hasKey = (obj, path) => {
const hasKey = (obj:any, path:string) => {
if (!obj || !path) return null;
const keys = path.split('.');
let result = obj;
for (let i = 0; i < keys.length; i++) {
@ -719,12 +763,11 @@ const hasKey = (obj, path) => {
}
return false;
}
const topBtnClick = (btn) => {
console.log(btn)
const topBtnClick = (btn:any) => {
btn.handler();
changeCellInfo()
}
const changePageColor = (val, key) => {
const changePageColor = (val:any, key:string) => {
const split = key.split(".");
if (split.length === 1) {
that.pageConfig[split[0]] = val;
@ -738,7 +781,7 @@ const changePageColor = (val, key) => {
changePageConfig()
}
const changeColor = (val, key) => {
const changeColor = (val:any, key:string) => {
const split = key.split(".");
if (split.length === 1) {
that.cellInfo[split[0]] = val;
@ -751,9 +794,11 @@ const changeColor = (val, key) => {
}
changeCellInfo()
}
const alignBtnList = computed(() => {
let arr = [];
if (hasKey(that.cellInfo, 'data.style.text.textAlign')) {
arr.push({
key: 'data.style.text.textAlign_start',
tipsWidth: 80,
@ -871,6 +916,7 @@ const topBtnList = computed(() => {
}
if (hasKey(that.cellInfo, 'data.style.text.fontStyle')) {
//@ts-ignore
arr.push({
key: 'data.style.text.fontStyle',
tipsWidth: 80,
@ -891,22 +937,6 @@ const topBtnList = computed(() => {
return arr
})
const borderRadiusRangeList = computed(() => {
let arr = [];
arr.push({
value: '0',
label: '0'
})
arr.push({
value: '10px',
label: '10px'
})
arr.push({
value: '20px',
label: '20px'
})
return arr;
})
const fontFamilyList = computed(() => {
return [
{
@ -986,10 +1016,8 @@ const strokeWidthList = computed(() => {
const angleList = computed(() => {
const arr = []
for (let i = 0; i < 360; i++) {
arr.push({
value: i,
label: `${i}`
})
//@ts-ignore
arr.push({ value: i, label: `${i}` })
}
return arr
})
@ -1044,10 +1072,20 @@ const fontSizeList = computed(() => {
return arr
})
const sizeModeList = [
{
value: '15_50',
label: '洗水唛15 *50'
},
{
value: '20_50',
label: '洗水唛20 *50'
},
{
value: '75_100',
label: '吊牌75 *50'
}, {
},
{
value: '50_750',
label: '吊牌50 *750'
},
@ -1076,12 +1114,7 @@ const sizeModeList = [
}
]
const zoomList = [
{
value: 0.3,
label: '0.3'
},
{
value: 0.5,
label: '0.5'
@ -1112,7 +1145,9 @@ const that = reactive({
containerId: nextId(),
currentZoom: 1,
showDialog: false,
showOrderByDialog: false,
sizeMode: '',
propList: [ ],
borderAngle: [0, 0, 0, 0],
pageConfig: {
background: {
@ -1151,11 +1186,11 @@ const that = reactive({
}
})
watch(() => that.pageConfig, () => {
emit('onChangePageConfig', that.pageConfig)
emit('changePageConfig', that.pageConfig)
})
watch(() => that.currentZoom, () => {
console.log("that.currentZoom", that.currentZoom)
emit('changeZoom', that.currentZoom.toFixed(2))
emit('changeZoom', (that.currentZoom).toFixed(2))
})
const updateBtnState = (state) => {
that.btnState = {
@ -1198,11 +1233,10 @@ const changeSize = () => {
changeCellInfo()
}
const changeCellInfo = (cellPos = undefined) => {
console.log("that.cellInfo", that.cellInfo)
emit('onChangeCell', that.cellInfo, cellPos)
emit('changeCell', that.cellInfo, cellPos)
}
const changePageConfig = () => {
emit('onChangePageConfig', that.pageConfig)
emit('changePageConfig', that.pageConfig)
}
const layout = reactive({
draftDesignState: false,
@ -1321,16 +1355,24 @@ const setGraph = (g)=>{
const clickItem = (item)=>{
emit('selectCell', item)
}
const updatePropList = (propList: any[])=>{
that.propList = propList
}
const propListChange=()=>{
console.log("propListChange@@")
emit('propListChange', that.propList)
}
defineExpose({
updateBtnState,
updateZoom,
updatePageConfig,
selectedNode,
setGraph,
updateCellList
updateCellList,
updatePropList
})
const handlerAddNode = (e, item) => {
emit("onAddNode", e, item)
emit("addNode", e, item)
}
</script>
@ -1532,10 +1574,11 @@ $panelZIndex: 10;
}
.center-panel {
overflow: hidden;
padding: 0;
width: 100%;
background-color: #6fa9ba;
height: 600px;
height: calc(86vh - 100px);
}
@ -1555,4 +1598,26 @@ $panelZIndex: 10;
transform: rotate(133deg);
}
.draggable-ghost {
background: #2ba4f8;
border: 2px solid #2ba4f8;
outline-width: 0;
height: 38px;
box-sizing: border-box;
font-size: 0;
content: '';
overflow: hidden;
padding: 0;
}
.order-by-item:hover{
cursor: move;
}
.order-by-item{
margin: 6px;
border: 1px solid #8e8b8b;
width: 100%;
padding: 10px
}
</style>

View File

@ -329,7 +329,6 @@ export default defineComponent({
}
}
}
console.log("text##", info)
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),

View File

@ -209,7 +209,6 @@ export default defineComponent({
}
}
}
console.log("text##", info)
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),

View File

@ -217,7 +217,6 @@ export default defineComponent({
}
}
}
console.log("text##", info)
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),

View File

@ -3,8 +3,8 @@
<div
:id="cellInfo.id"
class="node-content"
:class="{ 'node-content-group' : this.cellInfo.editMode && this.cellInfo.propGroupId }"
:title="this.cellInfo.propGroupName"
:class="{ 'node-content-group' : cellInfo.editMode && cellInfo.propGroupId }"
:title="cellInfo.propGroupName"
v-reSize="changSize">
<svg
style="position: absolute;
@ -14,7 +14,7 @@
:style="svgStyle"
>
<image
:href="cellInfo.style.shape.href" x="0" y="0"
:href="hrefBase64 || cellInfo.style.shape.href" x="0" y="0"
style="width: 100%;height: 100%;"
:stroke="cellInfo.style.shape.strokeColor"
:stroke-width="cellInfo.style.shape.strokeWidth"
@ -37,11 +37,11 @@
import {defineComponent} from 'vue'
import {
filterUnsafeHtml,
getSafeValue,
mergeDeepObject,
nextId, objectHasKey
nextId,
} from "@/components/DraftDesign/utils/FuncUtil";
import {getDraftDesignState, VueCellShapeType} from "@/components/DraftDesign/config";
import {convertImageToBase64} from "@/components/DraftDesign/utils/Dpi";
export default defineComponent({
@ -59,18 +59,23 @@ export default defineComponent({
svgStyle() {
const res = {
borderRadius: '',
width: `1000px`,
height: `1000px`,
transform: ''
}
res.width = `1000px`;
res.height = `1000px`;
const tmp = Math.min(this.sizeWInfo.width, this.sizeWInfo.height) - (this.cellInfo.style.shape.strokeWidth * 2)
const val = tmp
const w = (Math.max(this.sizeWInfo.width, 0)) / 1000;
const h = (Math.max(this.sizeWInfo.height, 0)) / 1000;
res.transform = `scaleX(${w}) scaleY(${h})`
return res;
},
ellipseStyle() {
let res = {};
let res = {
cx: 0,
cy: 0,
rx: 0,
ry: 0,
};
if (this.cellInfo.shape === VueCellShapeType.Ellipse) {
const w = this.sizeWInfo.width / 2
const h = this.sizeWInfo.height / 2
@ -91,7 +96,10 @@ export default defineComponent({
cellInfo: {
id: '',
label: '文字说明',
editMode: false,
showInput: false,
propGroupName: "",
propGroupId: null,
shape: VueCellShapeType.Rect,
style: {
shape: {
@ -116,6 +124,7 @@ export default defineComponent({
},
text: {
minWidth: 'max-content',
fontSize: 12,
minHeight: 'max-content',
}
}
@ -130,6 +139,7 @@ export default defineComponent({
}
},
fontSize: 12,
hrefBase64: null,
targetElement: null,
minSize: 2,
sizeWInfo: {
@ -181,11 +191,10 @@ export default defineComponent({
}
}
},
// @ts-ignore
setInput(val: boolean) {
},
changSize(size: any) {
console.log("cellInfo.label", this.cellInfo.label)
this.sizeWInfo.width = size.width;
this.sizeWInfo.height = size.height;
this.minSize = Math.min(size.height, size.width)
@ -200,11 +209,11 @@ export default defineComponent({
}
// this.nodeInfo.store.data.size.width = size.width;
console.log(" this.nodeInfo.width", this.nodeInfo)
this.fontSize = s;
this.changeFontSize();
},
changeFontSize() {
// @ts-ignore
this.cellInfo.style.text.fontSize = `${this.fontSize}px`
},
setCellInfo(info: any) {
@ -215,7 +224,7 @@ export default defineComponent({
}
}
}
console.log("image#####", info)
// @ts-ignore
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),
@ -246,7 +255,12 @@ export default defineComponent({
text: {}
}
}, info)
if(this.cellInfo.style.shape.href){
convertImageToBase64(this.cellInfo.style.shape.href).then((res)=>{
// @ts-ignore
this.hrefBase64 = res
})
}
setTimeout(() => {
if (this.nodeInfo.store && this.nodeInfo.store.data) {
this.nodeInfo.store.data.data = this.cellInfo

View File

@ -249,7 +249,6 @@ export default defineComponent({
}
}
}
console.log("text##", info)
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),

View File

@ -255,7 +255,6 @@ export default defineComponent({
}
}
}
console.log("text##", info)
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),

View File

@ -239,7 +239,6 @@ export default defineComponent({
}
}
}
console.log("text##", info)
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),

View File

@ -240,7 +240,6 @@ export default defineComponent({
}
}
}
console.log("text##", info)
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),

View File

@ -52,8 +52,9 @@ export default defineComponent({
data() {
return {
textElId: nextId(),
cellInfo: {
id: '',
id: nextId(),
label: '新文本',
showInput: false,
style: {
@ -100,18 +101,15 @@ export default defineComponent({
mounted() {
const node = (this as any).getNode()
this.nodeInfo = node
if (node && node.data) {
this.setCellInfo(node.data)
}
node.on('change:data', ({current}) => {
this.setCellInfo(current)
})
},
unmounted() {
},
methods: {
safeHtml(val) {
@ -208,7 +206,6 @@ export default defineComponent({
}
}
}
this.cellInfo = mergeDeepObject({
showInput: false,
id: nextId(),
@ -237,15 +234,14 @@ export default defineComponent({
}
}, info)
console.log("text##", this.nodeInfo)
setTimeout(() => {
if (this.nodeInfo.store && this.nodeInfo.store.data) {
this.nodeInfo.store.data.data = this.cellInfo
}
}, 300)
// setTimeout(() => {
// if (this.nodeInfo.store && this.nodeInfo.store.data) {
// this.nodeInfo.store.data.data = this.cellInfo
// }
// }, 300)
this.fontSize = parseInt(getSafeValue(this.cellInfo.style.text, 'fontSizeVal') || '12')
console.log(" this.fontSize", this.cellInfo)
// this.fontSize = parseInt(getSafeValue(this.cellInfo.style.text, 'fontSize') || '14')
this.setInput(this.cellInfo.showInput)
this.cellInfo.editMode = getDraftDesignState();

View File

@ -73,7 +73,7 @@ export const graphOptions = (elId:string, viewMode = false) => {
// 显示网格
grid: {
visible: false,
visible: true,
size: 2, // 网格大小
type: 'doubleMesh', // // 'dot' | 'fixedDot' | 'mesh'
args: [{
@ -87,7 +87,7 @@ export const graphOptions = (elId:string, viewMode = false) => {
},
// 滚轮缩放 MouseWheel
mousewheel: {
enabled: true, zoomAtMousePosition: true, modifiers: ['ctrl', 'meta'], maxScale: 10, minScale: 0.3
enabled: true, zoomAtMousePosition: true, modifiers: ['ctrl', 'meta'], maxScale: 10, minScale: 0.5
},
panning: {
// 画布移动

View File

@ -9,10 +9,80 @@
<link rel="stylesheet" href="style.css"></head>
<body>
<div class="bgc1 clearfix">
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;62)</small></h1>
<h1 class="mhmm mvm"><span class="fgc1">Font Name:</span> icomoon <small class="fgc1">(Glyphs:&nbsp;67)</small></h1>
</div>
<div class="clearfix mhl ptl">
<h1 class="mvm mtn fgc1">Grid Size: Unknown</h1>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-lk_order_by"></span>
<span class="mls"> icon-lk_order_by</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e93e" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe93e;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-lk_search"></span>
<span class="mls"> icon-lk_search</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e93f" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe93f;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-lk_reduce"></span>
<span class="mls"> icon-lk_reduce</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e940" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe940;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-lk_edit"></span>
<span class="mls"> icon-lk_edit</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e941" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe941;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-lk_add"></span>
<span class="mls"> icon-lk_add</span>
</div>
<fieldset class="fs0 size1of1 clearfix hidden-false">
<input type="text" readonly value="e942" class="unit size1of2" />
<input type="text" maxlength="1" readonly value="&#xe942;" class="unitRight size1of2 talign-right" />
</fieldset>
<div class="fs0 bshadow0 clearfix hidden-true">
<span class="unit pvs fgc1">liga: </span>
<input type="text" readonly value="" class="liga unitRight" />
</div>
</div>
<div class="glyph fs1">
<div class="clearfix bshadow0 pbs">
<span class="icon-lk_cell_clear"></span>

View File

@ -69,4 +69,9 @@
<glyph unicode="&#xe93b;" glyph-name="lk_cell_clear" d="M389.6 115.2c-3.8-7.7-15.3-11.5-23-11.5h-3.8c-7.7 0-19.1 3.8-26.8 11.5l-160.7 160.6c-3.8 7.7-7.7 15.3-7.7 26.8s3.8 19.1 11.5 26.8l248.7 248.7c0-15.3 7.7-26.8 19.1-38.3l160.7-160.7c11.5-11.5 23-15.3 34.4-19.1l-252.4-244.8zM710.9 383l160.7 160.7c30.6 30.6 30.6 76.5 0 107.1l-160.7 160.6c-7.7 7.7-15.3 11.5-26.8 15.3-3.8 3.8-15.3 3.8-26.8 3.8s-19.1 0-26.8-3.8c-7.7-3.8-19.1-7.7-26.8-15.3l-156.7-164.5-294.6-294.5c-30.6-30.6-30.6-76.5 0-107.1l160.7-160.7c11.5-11.5 30.6-19.1 45.9-19.1h459v38.2h-382.5l275.4 279.3z" />
<glyph unicode="&#xe93c;" glyph-name="lk_cell_tree" d="M807.36 209.6c-55.872 0-102.656-38.336-116.352-89.92h-382.272v243.648h384.896c16.77-47.653 61.297-81.242 113.7-81.472h0.028c66.687 0.036 120.75 54.032 120.896 120.69v0.014c-0.073 66.74-54.156 120.823-120.889 120.896h-0.007c-58.724-0.164-107.594-42.15-118.404-97.733l-0.124-0.763h-380.16v141.824c86.531 15.073 151.48 89.616 151.48 179.328 0 100.454-81.434 181.888-181.888 181.888-0.11 0-0.22 0-0.329 0h0.017c-100.614-0.073-182.163-81.599-182.272-182.198v-0.010c0.234-89.48 64.999-163.765 150.201-178.784l1.095-0.16v-509.76h61.632v0.96h382.272c14.057-52.065 60.772-89.781 116.329-89.984h0.023c65.049 2.149 116.964 55.397 116.964 120.77 0 65.329-51.844 118.548-116.634 120.76l-0.202 0.005zM807.36 461.76c32.64 0 59.264-26.56 59.264-59.2-1.512-31.524-27.438-56.511-59.2-56.511s-57.688 24.986-59.195 56.375l-0.005 0.135c0 32.64 26.496 59.2 59.136 59.2zM157.376 745.792c0 66.496 54.144 120.576 120.64 120.576 66.565-0.036 120.512-54.006 120.512-120.576 0-66.592-53.984-120.576-120.576-120.576s-120.576 53.984-120.576 120.576v0zM807.36 29.632c-32.654 0.055-59.104 26.538-59.104 59.2 0 32.695 26.505 59.2 59.2 59.2 0.034 0 0.067 0 0.101 0h-0.005c32.654-0.055 59.104-26.538 59.104-59.2 0-32.695-26.505-59.2-59.2-59.2-0.034 0-0.067 0-0.101 0h0.005z" />
<glyph unicode="&#xe93d;" glyph-name="lk_cell_add" d="M694.857 539.429c-0.008 0-0.017 0-0.026 0-136.976 0-250.501-100.407-270.989-231.635l-0.199-1.544h-131.072c-0.022 0-0.047 0-0.073 0-17.653 0-31.963 14.311-31.963 31.963 0 0.026 0 0.051 0 0.077v-0.004 416.037h332.654c16.648-40.658 55.911-68.779 101.742-68.779 60.553 0 109.641 49.088 109.641 109.641 0 0.086 0 0.171 0 0.257v-0.013c-0.034 60.568-49.142 109.654-109.714 109.654-45.729 0-84.923-27.976-101.401-67.747l-0.267-0.728h-538.331v-82.286h123.465v-416.037c0-63.122 51.2-114.322 114.249-114.322h131.072c20.719-132.738 134.227-233.106 271.177-233.106 151.484 0 274.286 122.802 274.286 274.286 0 151.471-122.781 274.265-274.247 274.286h-0.002zM841.143 235.447c0-5.049-4.093-9.143-9.143-9.143v0h-98.304v-98.304c0-5.049-4.093-9.143-9.143-9.143v0h-59.392c-5.049 0-9.143 4.093-9.143 9.143v0 98.304h-98.304c-5.049 0-9.143 4.093-9.143 9.143v0 59.392c0 5.047 4.096 9.143 9.143 9.143h98.304v98.304c0 5.047 4.096 9.143 9.143 9.143h59.392c5.047 0 9.143-4.096 9.143-9.143v-98.304h98.304c5.047 0 9.143-4.096 9.143-9.143v-59.392z" />
<glyph unicode="&#xe93e;" glyph-name="lk_order_by" d="M341.333 448.014c0 23.564 19.103 42.667 42.667 42.667v0h512c23.561-0.004 42.66-19.105 42.66-42.667s-19.099-42.663-42.66-42.667h-512c-23.564 0-42.667 19.103-42.667 42.667v0zM341.333 106.681c0 23.564 19.103 42.667 42.667 42.667v0h512c23.564 0 42.667-19.103 42.667-42.667s-19.103-42.667-42.667-42.667v0h-512c-23.564 0-42.667 19.103-42.667 42.667v0zM341.333 789.348c0 23.564 19.103 42.667 42.667 42.667v0h512c23.561-0.004 42.66-19.105 42.66-42.667s-19.099-42.663-42.66-42.667h-512c-23.564 0-42.667 19.103-42.667 42.667v0zM256 448.014c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333v0c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333v0zM256 789.348c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333v0c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333v0zM256 106.681c0-47.128-38.205-85.333-85.333-85.333s-85.333 38.205-85.333 85.333v0c0 47.128 38.205 85.333 85.333 85.333s85.333-38.205 85.333-85.333v0z" />
<glyph unicode="&#xe93f;" glyph-name="lk_search" d="M689 364c30 47.2 47.6 103.2 47.6 163.4 0 168.2-136.2 304.6-304.2 304.6-168.2 0-304.4-136.4-304.4-304.6s136.2-304.6 304.2-304.6c61 0 117.8 18 165.4 48.8l13.8 9.6 217.2-217.2 67.4 68.6-217 217.2 10 14.2zM602.8 697.6c45.4-45.4 70.4-105.8 70.4-170s-25-124.6-70.4-170c-45.4-45.4-105.8-70.4-170-70.4s-124.6 25-170 70.4c-45.4 45.4-70.4 105.8-70.4 170s25 124.6 70.4 170c45.4 45.4 105.8 70.4 170 70.4s124.6-25 170-70.4z" />
<glyph unicode="&#xe940;" glyph-name="lk_reduce" d="M188.8 824.3c-29.7 0-53.8-24.1-53.8-53.7v-644.7c0-29.7 24.1-53.7 53.8-53.7h645.4c29.7 0 53.8 24.1 53.8 53.7v644.7c0 29.7-24.1 53.7-53.8 53.7h-645.4zM175.8 895.4h671.5c61.8 0 111.9-50.1 111.9-111.8v-670.8c0-61.7-50.1-111.8-111.9-111.8h-671.5c-61.8 0-111.9 50-111.9 111.8v670.8c0 61.8 50.1 111.8 111.9 111.8zM673 412h-322c-19.8 0-36 16.2-36 36s16.2 36 36 36h322c19.8 0 36-16.2 36-36s-16.2-36-36-36z" />
<glyph unicode="&#xe941;" glyph-name="lk_edit" d="M860 456c-19.9 0-36-16.1-36-36 0-1.4 0.1-2.7 0.2-4h-0.2v-344h-688v688h376c19.9 0 36 16.1 36 36s-16.1 36-36 36h-376c-39.8 0-72-32.2-72-72v-688c0-39.8 32.2-72 72-72h688c39.8 0 72 32.2 72 72v344h-0.2c0.1 1.3 0.2 2.6 0.2 4 0 19.9-16.1 36-36 36zM1002.7 859.7l-79.3 79.3c-28.1 28.1-73.9 27.9-102-0.2l-397.2-397.2c-2.9-2.9-5.2-6.4-6.8-10.2l-99.8-235.4c-5.6-13.2-1.7-26.5 6.8-35.1s21.9-12.5 35.2-6.9l235.5 99.7c3.8 1.6 7.2 3.9 10.2 6.8l397.2 397.2c28.1 28.1 28.3 73.9 0.2 102zM559.8 417l-137.4-58.2 58.2 137.4 278.8 278.8 79.2-79.2-278.8-278.8zM951.5 808.7l-62-62-79.2 79.2 62.2 62.2 79.2-79.2-0.2-0.2z" />
<glyph unicode="&#xe942;" glyph-name="lk_add" d="M780.007 1.27h-536.104c-98.6 0-178.613 79.969-178.613 178.613v536.105c0 98.731 80.013 178.745 178.613 178.745h536.105c98.773 0 178.701-80.013 178.701-178.745v-536.105c-0.001-98.643-79.928-178.613-178.702-178.613v0zM869.445 671.312c0 74.038-60.119 134.069-134.113 134.069h-446.753c-73.994 0-134.028-60.031-134.028-134.069v-446.666c0-74.124 60.033-134.069 134.028-134.069h446.753c73.994 0 134.113 59.945 134.113 134.069v446.666zM556.631 224.646h-89.351v178.613h-178.701v89.44h178.701v178.613h89.351v-178.613h178.701v-89.44h-178.701v-178.613z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 48 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
@font-face {
font-family: 'icomoon';
src: url('fonts/icomoon.eot?8dhkdr');
src: url('fonts/icomoon.eot?8dhkdr#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?8dhkdr') format('truetype'),
url('fonts/icomoon.woff?8dhkdr') format('woff'),
url('fonts/icomoon.svg?8dhkdr#icomoon') format('svg');
src: url('fonts/icomoon.eot?5gmwxn');
src: url('fonts/icomoon.eot?5gmwxn#iefix') format('embedded-opentype'),
url('fonts/icomoon.ttf?5gmwxn') format('truetype'),
url('fonts/icomoon.woff?5gmwxn') format('woff'),
url('fonts/icomoon.svg?5gmwxn#icomoon') format('svg');
font-weight: normal;
font-style: normal;
font-display: block;
@ -25,6 +25,21 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-lk_order_by:before {
content: "\e93e";
}
.icon-lk_search:before {
content: "\e93f";
}
.icon-lk_reduce:before {
content: "\e940";
}
.icon-lk_edit:before {
content: "\e941";
}
.icon-lk_add:before {
content: "\e942";
}
.icon-lk_cell_clear:before {
content: "\e93b";
}

View File

@ -1,4 +1,7 @@
// 获取设备像素比
import * as FileApi from "@/api/infra/file";
import {replaceDomain} from "@/utils";
const dppx = window.devicePixelRatio ||
(window.matchMedia && window.matchMedia("(min-resolution: 2dppx), (-webkit-min-device-pixel-ratio: 1.5),(-moz-min-device-pixel-ratio: 1.5),(min-device-pixel-ratio: 1.5)").matches ? 2 : 1) ||
1;
@ -33,8 +36,30 @@ export function calcDpi (width, height, screenSize=16, opt = 'd') {
export function calcDpiFromSize( screenSize=16,opt = 'd'){
return calcDpi(widthValue, heightValue, screenSize, opt);
}
const tmpImage = {}
export function convertImageToBase64(url) {
return new Promise(async (resolve, reject)=>{
FileApi.getDomain().then(async (domain) => {
url = replaceDomain(domain, url)
if(tmpImage[url]){
resolve(tmpImage[url]);
console.log('缓存')
return;
}
try {
const response = await fetch(url);
const blob = await response.blob();
const base = `data:${response.headers.get('content-type')};base64,${btoa(String.fromCharCode(...new Uint8Array(await blob.arrayBuffer())))}`
tmpImage[url] = base
resolve(base)
}catch (e) {
resolve(url);
}
})
})
}

View File

@ -237,12 +237,12 @@ export function toHighlightText(originalText: string, keyword: string) {
export const parseKeywordList = (keyword: string): [] => {
//@ts-ignore
const arr = [];
const arr: string[] = [];
if (keyword) {
// 去空串
keyword = keyword.replace(/\s/g, '');
const strings = keyword.toUpperCase().split("");
strings.forEach(s => {
strings.forEach((s:string) => {
//@ts-ignore
if (!arr.includes(s)) {
arr.push(s);

View File

@ -5,9 +5,6 @@
<el-row :gutter="16" justify="space-between">
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
<div class="flex items-center">
<el-avatar :src="avatar" :size="70" class="mr-16px">
<img src="@/assets/imgs/avatar.gif" alt="" />
</el-avatar>
<div>
<div class="text-20px">
{{ t('workplace.welcome') }} {{ username }} {{ t('workplace.happyDay') }}
@ -18,158 +15,12 @@
</div>
</div>
</el-col>
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
<div class="h-70px flex items-center justify-end lt-sm:mt-10px">
<div class="px-8px text-right">
<div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div>
<CountTo
class="text-20px"
:start-val="0"
:end-val="totalSate.project"
:duration="2600"
/>
</div>
<el-divider direction="vertical" />
<div class="px-8px text-right">
<div class="mb-16px text-14px text-gray-400">{{ t('workplace.toDo') }}</div>
<CountTo
class="text-20px"
:start-val="0"
:end-val="totalSate.todo"
:duration="2600"
/>
</div>
<el-divider direction="vertical" border-style="dashed" />
<div class="px-8px text-right">
<div class="mb-16px text-14px text-gray-400">{{ t('workplace.access') }}</div>
<CountTo
class="text-20px"
:start-val="0"
:end-val="totalSate.access"
:duration="2600"
/>
</div>
</div>
</el-col>
</el-row>
</el-skeleton>
</el-card>
</div>
<el-row class="mt-8px" :gutter="8" justify="space-between">
<el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px">
<el-card shadow="never">
<template #header>
<div class="h-3 flex justify-between">
<span>{{ t('workplace.project') }}</span>
<el-link
type="primary"
:underline="false"
href="https://github.com/yudaocode"
target="_blank"
>
{{ t('action.more') }}
</el-link>
</div>
</template>
<el-skeleton :loading="loading" animated>
<el-row>
<el-col
v-for="(item, index) in projects"
:key="`card-${index}`"
:xl="8"
:lg="8"
:md="8"
:sm="24"
:xs="24"
>
<el-card shadow="hover" class="mr-5px mt-5px">
<div class="flex items-center">
<Icon :icon="item.icon" :size="25" class="mr-8px" />
<span class="text-16px">{{ item.name }}</span>
</div>
<div class="mt-12px text-9px text-gray-400">{{ t(item.message) }}</div>
<div class="mt-12px flex justify-between text-12px text-gray-400">
<span>{{ item.personal }}</span>
<span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span>
</div>
</el-card>
</el-col>
</el-row>
</el-skeleton>
</el-card>
<el-card shadow="never" class="mt-8px">
<el-skeleton :loading="loading" animated>
<el-row :gutter="20" justify="space-between">
<el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-8px">
<el-skeleton :loading="loading" animated>
<Echart :options="pieOptionsData" :height="280" />
</el-skeleton>
</el-card>
</el-col>
<el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">
<el-card shadow="hover" class="mb-8px">
<el-skeleton :loading="loading" animated>
<Echart :options="barOptionsData" :height="280" />
</el-skeleton>
</el-card>
</el-col>
</el-row>
</el-skeleton>
</el-card>
</el-col>
<el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-8px">
<el-card shadow="never">
<template #header>
<div class="h-3 flex justify-between">
<span>{{ t('workplace.shortcutOperation') }}</span>
</div>
</template>
<el-skeleton :loading="loading" animated>
<el-row>
<el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-8px">
<div class="flex items-center">
<Icon :icon="item.icon" class="mr-8px" />
<el-link type="default" :underline="false" @click="setWatermark(item.name)">
{{ item.name }}
</el-link>
</div>
</el-col>
</el-row>
</el-skeleton>
</el-card>
<el-card shadow="never" class="mt-8px">
<template #header>
<div class="h-3 flex justify-between">
<span>{{ t('workplace.notice') }}</span>
<el-link type="primary" :underline="false">{{ t('action.more') }}</el-link>
</div>
</template>
<el-skeleton :loading="loading" animated>
<div v-for="(item, index) in notice" :key="`dynamics-${index}`">
<div class="flex items-center">
<el-avatar :src="avatar" :size="35" class="mr-16px">
<img src="@/assets/imgs/avatar.gif" alt="" />
</el-avatar>
<div>
<div class="text-14px">
<Highlight :keys="item.keys.map((v) => t(v))">
{{ item.type }} : {{ item.title }}
</Highlight>
</div>
<div class="mt-16px text-12px text-gray-400">
{{ formatTime(item.date, 'yyyy-MM-dd') }}
</div>
</div>
</div>
<el-divider />
</div>
</el-skeleton>
</el-card>
</el-col>
</el-row>
</template>
<script lang="ts" setup>
import { set } from 'lodash-es'

View File

@ -184,7 +184,7 @@ const loginData = reactive({
captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE,
tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
loginForm: {
tenantName: '',
tenantName: '芋道源码',
username: '',
password: '',
captchaVerification: '',

View File

@ -86,7 +86,7 @@ const formData = ref({
brandField: undefined,
website: undefined,
intro: undefined,
locale: undefined,
locale: 'zh-CN',
remark: undefined,
})
const formRules = reactive({
@ -147,7 +147,7 @@ const resetForm = () => {
brandField: undefined,
website: undefined,
intro: undefined,
locale: undefined,
locale: 'zh-CN',
remark: undefined,
}
formRef.value?.resetFields()

View File

@ -72,6 +72,7 @@ const { t } = useI18n() // 国际化
const message = useMessage() //
const route = useRoute() //
const draftDesignRef = ref()
const designPropEditRef = ref()
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
@ -106,6 +107,7 @@ const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
// designPropEditRef.value.init(1);
resetForm()
//
if (id) {
@ -125,6 +127,7 @@ const open = async (type: string, id?: number) => {
formLoading.value = false
}
}else {
draftDesignRef.value.init(true, {})
}
}
@ -176,6 +179,5 @@ const resetForm = () => {
propDefault: undefined,
}
formRef.value?.resetFields()
draftDesignRef.value.clearData()
}
</script>

View File

@ -1,4 +1,6 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
@ -44,7 +46,7 @@
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.LANGUAGE_LOCALE)"
:key="dict.value"
:key="`${dict.value}`"
:label="dict.label"
:value="dict.value"
/>
@ -179,13 +181,15 @@ const queryParams = reactive({
remark: undefined,
details: undefined,
propDefault: undefined,
createTime: [],
createTime: '',
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
console.log("list")
loading.value = true
try {
const data = await DraftDesignDataApi.getDraftDesignDataPage(queryParams)
@ -210,6 +214,7 @@ const resetQuery = () => {
/** 添加/修改操作 */
const formRef = ref()
// @ts-ignore
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}

View File

@ -0,0 +1,214 @@
<template>
<Dialog
:title="dialogTitle"
width="68vw"
v-model="dialogVisible">
<el-scrollbar height="60vh">
<div>
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="保养项名称" prop="value">
<el-input :name="`_$_ProductCareItemForm_value`" :autocomplete="`_$_ProductCareItemForm_value`" v-model="formData.value" clearable placeholder="请输入保养项名称" />
</el-form-item>
<el-form-item label="icon图标" prop="iconUrl">
<DraftDesignImageLibDialog v-model="formData.iconUrl" placeholder="请输入icon图标" @submit="changeIcon" />
</el-form-item>
<el-form-item label="品牌通用" prop="isAll">
<el-checkbox v-model="formData.isAll">该保养说明适用于所有品牌</el-checkbox>
</el-form-item>
<el-form-item v-if="!formData.isAll" label="品牌" prop="brandIds">
<span>改说明只适用于指定品牌</span>
<el-input v-model="formData.brandIds" placeholder="请输入品牌" />
</el-form-item>
<el-form-item label="作用于组合" prop="isCombo">
<el-checkbox v-model="formData.isCombo">是否只作用于组合(洗涤说明icon和文本)</el-checkbox>
</el-form-item>
<el-form-item label="多语言配置" prop="lang_mapping">
<div class="w-full">
<el-collapse accordion v-model="langeActiveName">
<el-collapse-item name="1">
<template #title>
多语言对照列表
</template>
<div>
<el-button @click="addLangMapping">添加</el-button>
<el-table :data="that.langList" :stripe="true" :show-overflow-tooltip="true">
<el-table-column prop="locale" label="地区语言" width="150" >
<template #default="scope">
<el-select v-model="scope.row.locale" placeholder="地区语言">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.LANGUAGE_LOCALE)"
:key="`${dict.value}`"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="value" label="多语言内容" >
<template #default="scope">
<el-input v-model="scope.row.value" placeholder="请输入多语言内容" />
</template>
</el-table-column>
<el-table-column prop="value" label="操作" >
<template #default="{$index}">
<el-button type="danger" @click="removeRow($index) " >删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-collapse-item>
</el-collapse>
</div>
</el-form-item>
<el-form-item label="语言标识" prop="locale">
<el-select v-model="formData.locale" placeholder="请选择语言标识">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.LANGUAGE_LOCALE)"
:key="`${dict.value}`"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="启用状态" prop="enabled">
<el-checkbox v-model="formData.enabled">是否启用</el-checkbox>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" />
</el-form-item>
</el-form>
</div>
</el-scrollbar>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading">{{t('common.ok')}}</el-button>
<el-button @click="dialogVisible = false">{{ t('common.cancel') }}</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { ProductCareItemApi, ProductCareItemVO } from '@/api/oms/productcareitem'
import DraftDesignImageLibDialog from "@/components/Dialog/src/DraftDesignImageLibDialog/index.vue";
/** 产品保养项 表单 */
defineOptions({ name: 'ProductCareItemForm' })
const { t } = useI18n() //
const message = useMessage() //
const dialogVisible = ref(false) //
const imageVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const formData = ref({
id: undefined,
value: undefined,
iconUrl: '',
brandIds: undefined,
isCombo: false,
isAll: true,
locale: 'zh-CN',
enabled: true,
remark: "",
})
const langeActiveName = ref('1');
const formRules = reactive({
value: [{ required: true, message: '说明不能为空', trigger: 'blur' }],
})
const formRef = ref() // Ref
const that = reactive({
langList: [{
locale: '',
value: '',
}],
})
const addLangMapping = () => {
that.langList.push({
locale: '',
value: '',
})
}
const removeRow = (index) => {
}
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
//
if (id) {
formLoading.value = true
try {
formData.value = await ProductCareItemApi.getProductCareItem(id)
} finally {
formLoading.value = false
}
}
}
const changeIcon = (info)=>{
console.log("info",info)
}
defineExpose({ open }) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
const submitForm = async () => {
//
await formRef.value.validate()
//
formLoading.value = true
try {
const data = formData.value as unknown as ProductCareItemVO
if (formType.value === 'create') {
await ProductCareItemApi.createProductCareItem(data)
message.success(t('common.createSuccess'))
} else {
await ProductCareItemApi.updateProductCareItem(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
//
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
value: undefined,
inputVal: '',
brandIds: undefined,
isCombo: false,
isAll: true,
locale: 'zh-CN',
enabled: true,
remark: "",
}
formRef.value?.resetFields()
}
</script>

View File

@ -0,0 +1,256 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-row>
<el-col :xs="24" :span="8">
<el-form-item label="名称" prop="value">
<el-input
v-model="queryParams.value"
placeholder="请输入保养项名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :span="8">
<el-form-item label="语言标识" prop="locale">
<el-select
v-model="queryParams.locale"
placeholder="请选择语言标识"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.LANGUAGE_LOCALE)"
:key="`${dict.value}`"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :span="8">
<el-form-item label="启用状态" prop="enabled">
<el-select
v-model="queryParams.enabled"
placeholder="请选择启用状态"
clearable
class="!w-240px"
>
<el-option label="启用" :value="true" />
<el-option label="停用" :value="false" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['oms:product-care-item:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['oms:product-care-item:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="保养项名称" align="center" prop="value" />
<el-table-column label="品牌通用" align="center" prop="isAll" >
<template #default="scope">
<div>
{{scope.row.isAll ? '是' : '否'}}
</div>
</template>
</el-table-column>
<el-table-column label="语言标识 " align="center" prop="locale">
<template #default="scope">
<dict-tag :type="DICT_TYPE.LANGUAGE_LOCALE" :value="scope.row.locale" />
</template>
</el-table-column>
<el-table-column label="只作用于组合" align="center" prop="isCombo" >
<template #default="scope">
<div>
{{scope.row.isCombo ? '是' : '否'}}
</div>
</template>
</el-table-column>
<el-table-column label="启用状态" align="center" prop="enabled" >
<template #default="scope">
<div>
{{scope.row.enabled ? '启用' : '停用'}}
</div>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column
label="创建时间"
align="center"
prop="createTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="primary"
@click="openForm('update', scope.row.id)"
v-hasPermi="['oms:product-care-item:update']"
>
编辑
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['oms:product-care-item:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<ProductCareItemForm ref="formRef" @success="getList" />
</template>
<script setup lang="ts">
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { ProductCareItemApi, ProductCareItemVO } from '@/api/oms/productcareitem'
import ProductCareItemForm from './ProductCareItemForm.vue'
/** 产品保养项 列表 */
defineOptions({ name: 'ProductCareItem' })
const message = useMessage() //
const { t } = useI18n() //
const loading = ref(true) //
const list = ref<ProductCareItemVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
value: undefined,
iconUrl: undefined,
brandIds: undefined,
isCombo: undefined,
isAll: undefined,
locale: undefined,
enabled: undefined,
remark: undefined,
createTime: [],
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
const data = await ProductCareItemApi.getProductCareItemPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
/** 添加/修改操作 */
const formRef = ref()
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await ProductCareItemApi.deleteProductCareItem(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await ProductCareItemApi.exportProductCareItem(queryParams)
download.excel(data, '产品保养项 .xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 初始化 **/
onMounted(() => {
getList()
})
</script>

View File

@ -1,39 +1,81 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-form-item label="产品编码" prop="code">
<el-input v-model="formData.code" placeholder="请输入产品编码" />
</el-form-item>
<el-form-item label="产品名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入产品名称" />
</el-form-item>
<el-form-item label="品牌" prop="brandId">
<el-input v-model="formData.brandId" placeholder="请输入品牌" />
</el-form-item>
<el-form-item label="产品类型id" prop="productTypeId">
<el-input v-model="formData.productTypeId" placeholder="请输入产品类型id" />
</el-form-item>
<el-form-item label="启用状态" prop="enabled">
<el-radio-group v-model="formData.enabled">
<el-radio label="1">请选择字典生成</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="详情介绍" prop="details">
<Editor v-model="formData.details" height="150px" />
</el-form-item>
<el-form-item label="封面" prop="cover">
<UploadImg v-model="formData.cover" />
</el-form-item>
</el-form>
<Dialog
:title="dialogTitle"
width="70vw"
top="2%"
v-model="dialogVisible">
<div class="w-full">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-row>
<el-col :span="12" :xs="24">
<el-form-item label="产品编码" prop="code">
<div class="flex w-full">
<el-input
v-model="formData.code"
placeholder="系统编码为空时自动生成"
:disabled="!inputCode"/>
<el-button @click=" inputCode = true">手动输入</el-button>
</div>
</el-form-item>
</el-col>
<el-col :span="12" :xs="24">
<el-form-item label="启用状态" prop="enabled">
<el-switch
v-model="formData.enabled"
inline-prompt
:active-value="true"
:inactive-value="false"/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12" :xs="24">
<el-form-item label="产品名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入产品名称"/>
</el-form-item>
</el-col>
<el-col :span="12" :xs="24">
<el-form-item label="设计稿" prop="draftDesignDataId">
<DraftDesignDataListDialog v-model="formData.draftDesignDataId" />
</el-form-item>
</el-col>
<el-form-item label="封面" prop="cover">
<UploadImg v-model="formData.cover"/>
</el-form-item>
</el-row>
<el-row>
<el-col :span="12" :xs="24">
<el-form-item label="品牌" prop="brandId">
<BrandDataListDialog v-model="formData.brandId" placeholder="请选择品牌" clearable />
</el-form-item>
</el-col>
<el-col :span="12" :xs="24">
<el-form-item label="产品类型" prop="productTypeId">
<ProductTypeDataListDialog v-model="formData.productTypeId" placeholder="请选择产品类型" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="formData.remark" placeholder="请输入备注"/>
</el-form-item>
<el-form-item label="详情介绍" prop="details">
<Editor v-model="formData.details" height="150px"/>
</el-form-item>
</el-form>
</div>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
@ -41,31 +83,34 @@
</Dialog>
</template>
<script setup lang="ts">
import { ProductInfoApi, ProductInfoVO } from '@/api/oms/productinfo'
import {ProductInfoApi, ProductInfoVO} from '@/api/oms/productinfo'
import DraftDesignDataListDialog from "@/components/Dialog/src/DraftDesignDataListDialog/index.vue";
import BrandDataListDialog from "@/components/Dialog/src/BrandDataListDialog/index.vue";
/** 产品资料 表单 */
defineOptions({ name: 'ProductInfoForm' })
defineOptions({name: 'ProductInfoForm'})
const { t } = useI18n() //
const {t} = useI18n() //
const message = useMessage() //
const showVisible = ref(false);
const dialogVisible = ref(false) //
const dialogTitle = ref('') //
const formLoading = ref(false) // 12
const formType = ref('') // create - update -
const inputCode = ref(false);
const formData = ref({
id: undefined,
code: undefined,
name: undefined,
cover: undefined,
brandId: undefined,
productTypeId: undefined,
enabled: undefined,
draftDesignDataId: undefined,
enabled: true,
remark: undefined,
details: undefined,
cover: undefined,
})
const formRules = reactive({
name: [{ required: true, message: '产品名称不能为空', trigger: 'blur' }],
name: [{required: true, message: '产品名称不能为空', trigger: 'blur'}],
})
const formRef = ref() // Ref
@ -85,7 +130,7 @@ const open = async (type: string, id?: number) => {
}
}
}
defineExpose({ open }) // open
defineExpose({open}) // open
/** 提交表单 */
const emit = defineEmits(['success']) // success
@ -117,13 +162,14 @@ const resetForm = () => {
id: undefined,
code: undefined,
name: undefined,
cover: undefined,
brandId: undefined,
productTypeId: undefined,
enabled: undefined,
draftDesignDataId: undefined,
enabled: true,
remark: undefined,
details: undefined,
cover: undefined,
}
formRef.value?.resetFields()
}
</script>
</script>

View File

@ -35,15 +35,16 @@
class="!w-240px"
/>
</el-form-item>
<el-form-item label="产品类型id" prop="productTypeId">
<el-form-item label="产品类型" prop="productTypeId">
<el-input
v-model="queryParams.productTypeId"
placeholder="请输入产品类型id"
placeholder="请输入产品类型"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="启用状态" prop="enabled">
<el-select
v-model="queryParams.enabled"
@ -51,7 +52,7 @@
clearable
class="!w-240px"
>
<el-option label="请选择字典生成" value="" />
<el-option label="请选择字典生成" value=""/>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
@ -75,15 +76,22 @@
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px"/>
搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px"/>
重置
</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['oms:product-info:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 新增
<Icon icon="ep:plus" class="mr-5px"/>
新增
</el-button>
<el-button
type="success"
@ -92,7 +100,8 @@
:loading="exportLoading"
v-hasPermi="['oms:product-info:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
<Icon icon="ep:download" class="mr-5px"/>
导出
</el-button>
</el-form-item>
</el-form>
@ -100,16 +109,27 @@
<!-- 列表 -->
<ContentWrap>
<div class="text-red"> todo 功能待完善</div>
<DesignPreviewDialog ref="designPreviewDialogRef"/>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="id" align="center" prop="id" />
<el-table-column label="产品编码" align="center" prop="code" />
<el-table-column label="产品名称" align="center" prop="name" />
<el-table-column label="品牌" align="center" prop="brandId" />
<el-table-column label="产品类型id" align="center" prop="productTypeId" />
<el-table-column label="启用状态" align="center" prop="enabled" />
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="详情介绍" align="center" prop="details" />
<el-table-column label="id" align="center" prop="id"/>
<el-table-column label="产品编码" align="center" prop="code"/>
<el-table-column label="产品名称" align="center" prop="name"/>
<el-table-column label="封面" align="center" prop="cover">
<template #default="scope">
<el-image
v-if="scope.row.cover"
style="width: 64px; height: 64px"
:src="scope.row.cover"
:preview-src-list="[scope.row.cover]"
/>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="品牌" align="center" prop="brandName"/>
<el-table-column label="产品类型" align="center" prop="productTypeId"/>
<el-table-column label="启用状态" align="center" prop="enabled"/>
<el-table-column label="备注" align="center" prop="remark"/>
<el-table-column
label="创建时间"
align="center"
@ -117,8 +137,7 @@
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="封面" align="center" prop="cover" />
<el-table-column label="操作" align="center">
<el-table-column label="操作" width="200px" align="center">
<template #default="scope">
<el-button
link
@ -128,6 +147,16 @@
>
编辑
</el-button>
<el-button
link
type="primary"
@click="previewDraftDesign(scope.row.id)"
v-hasPermi="['oms:product-info:update']"
:disabled="!scope.row.draftDesignDataId"
>
预览稿件
</el-button>
<el-button
link
type="danger"
@ -149,21 +178,22 @@
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<ProductInfoForm ref="formRef" @success="getList" />
<ProductInfoForm ref="formRef" @success="getList"/>
</template>
<script setup lang="ts">
import { dateFormatter } from '@/utils/formatTime'
import {dateFormatter} from '@/utils/formatTime'
import download from '@/utils/download'
import { ProductInfoApi, ProductInfoVO } from '@/api/oms/productinfo'
import {ProductInfoApi, ProductInfoVO} from '@/api/oms/productinfo'
import ProductInfoForm from './ProductInfoForm.vue'
/** 产品资料 列表 */
defineOptions({ name: 'ProductInfo' })
defineOptions({name: 'ProductInfo'})
const message = useMessage() //
const { t } = useI18n() //
const {t} = useI18n() //
const designPreviewDialogRef = ref()
const loading = ref(true) //
const list = ref<ProductInfoVO[]>([]) //
const total = ref(0) //
@ -172,13 +202,14 @@ const queryParams = reactive({
pageSize: 10,
code: undefined,
name: undefined,
cover: undefined,
brandId: undefined,
productTypeId: undefined,
draftDesignDataId: undefined,
enabled: undefined,
remark: undefined,
details: undefined,
createTime: [],
cover: undefined,
})
const queryFormRef = ref() //
const exportLoading = ref(false) //
@ -223,7 +254,8 @@ const handleDelete = async (id: number) => {
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
} catch {
}
}
/** 导出按钮操作 */
@ -240,7 +272,9 @@ const handleExport = async () => {
exportLoading.value = false
}
}
const previewDraftDesign = (id) => {
designPreviewDialogRef.value.previewByProductId(id)
}
/** 初始化 **/
onMounted(() => {
getList()

View File

@ -184,10 +184,11 @@ const loginData = reactive({
captchaEnable: import.meta.env.VITE_APP_CAPTCHA_ENABLE,
tenantEnable: import.meta.env.VITE_APP_TENANT_ENABLE,
loginForm: {
tenantName: '',
tenantName: '芋道源码',
username: '',
password: '',
type: '999999',
// todo type: '999999',
type: '',
captchaVerification: '',
rememberMe: true //
}