fix: 自定义标签

This commit is contained in:
2026-03-03 15:40:21 +08:00
parent fe4e742551
commit 13fe5d9124
41 changed files with 2643 additions and 0 deletions

View File

@@ -0,0 +1,311 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="560px">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="110px"
v-loading="formLoading"
>
<el-form-item label="标签名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入标签名称" maxlength="50" show-word-limit />
<div class="form-tip">用于报表中展示的标签名称</div>
</el-form-item>
<el-form-item label="公式描述" prop="expression">
<el-input
v-model="formData.expression"
type="textarea"
:rows="2"
placeholder="请输入公式描述,如:${region_name}"
/>
<div class="form-tip">报表中引用的表达式支持 ${字段名} 格式</div>
</el-form-item>
<el-form-item label="标签颜色" prop="color">
<div class="color-field">
<div class="color-presets">
<span
v-for="c in presetColors"
:key="c"
class="color-chip"
:class="{ active: formData.color === c }"
:style="{ backgroundColor: c }"
@click="formData.color = c"
></span>
</div>
<div class="color-picker-wrap">
<el-color-picker v-model="formData.color" :predefine="presetColors" />
<el-input v-model="formData.color" placeholder="或输入颜色值" class="color-input" maxlength="20" />
</div>
</div>
<div class="form-tip">用于报表展示时的颜色标识</div>
</el-form-item>
<el-form-item label="代入参数" prop="useParams">
<el-radio-group v-model="formData.useParams">
<el-radio :value="1"></el-radio>
<el-radio :value="0"></el-radio>
</el-radio-group>
<div class="form-tip">是否将报表参数代入 SQL 中执行</div>
</el-form-item>
<el-form-item v-if="formData.useParams === 1" label="参数列表" prop="params">
<el-input v-model="formData.params" placeholder="如startDate,endDate" />
<div class="form-tip">需代入的参数名多个用逗号分隔</div>
</el-form-item>
<el-form-item label="SQL 脚本" prop="sqlScript">
<div class="sql-script-field">
<div v-if="formData.useParams === 1" class="param-insert-bar">
<span class="param-label">插入参数变量</span>
<el-select
v-model="selectedParamIndex"
:placeholder="paramList.length > 0 ? '选择参数后点击插入' : '请先填写上方参数列表'"
clearable
class="param-select"
:disabled="paramList.length === 0"
>
<el-option
v-for="(p, idx) in paramList"
:key="idx"
:label="`参数${idx + 1}: ${p}`"
:value="idx"
/>
</el-select>
<el-button
type="primary"
size="small"
:disabled="paramList.length === 0 || selectedParamIndex === null"
@click="selectedParamIndex != null ? insertParamVar(selectedParamIndex) : null"
>
插入
</el-button>
</div>
<el-input
ref="sqlScriptInputRef"
v-model="formData.sqlScript"
type="textarea"
:rows="4"
:placeholder="formData.useParams === 1 ? '请输入 SQL参数变量请通过上方下拉选择并插入' : '请输入 SQL 脚本'"
@blur="saveSqlCursor"
@focus="saveSqlCursor"
/>
<div v-if="formData.useParams === 1 && formData.sqlScript" class="sql-preview">
<span class="preview-label">预览参数以块显示</span>
<div class="sql-preview-content">
<template v-for="(seg, i) in sqlSegments" :key="i">
<el-tag v-if="seg.type === 'var'" type="primary" size="small" class="sql-var-tag">
{{ seg.text }}
</el-tag>
<span v-else class="sql-text">{{ seg.text }}</span>
</template>
</div>
</div>
</div>
<div class="form-tip">
<template v-if="formData.useParams === 1">参数变量通过下拉选择插入执行时 ${参数1}${参数2} 等将替换为对应参数值</template>
<template v-else>执行 SQL 获取标签选项第一列作为显示值</template>
</div>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { CustomTagApi, CustomTag, CustomTagType } from '@/api/ydoyun/customtag'
defineOptions({ name: 'CustomTagFormWithParams' })
const props = defineProps<{
tagType: CustomTagType
}>()
const { t } = useI18n()
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formLoading = ref(false)
const formType = ref('')
const formData = ref<Partial<CustomTag>>({
id: undefined,
type: undefined,
name: undefined,
expression: undefined,
color: undefined,
sqlScript: undefined,
useParams: 0,
params: undefined
})
const formRules = reactive({
name: [{ required: true, message: '标签名称不能为空', trigger: 'blur' }]
})
const formRef = ref()
const sqlScriptInputRef = ref()
const sqlCursorPos = ref({ start: 0, end: 0 })
const selectedParamIndex = ref<number | null>(null)
const paramList = computed(() => {
const p = formData.value.params
if (!p || typeof p !== 'string') return []
return p.split(',').map((s) => s.trim()).filter(Boolean)
})
const saveSqlCursor = () => {
const textarea = sqlScriptInputRef.value?.$el?.querySelector('textarea')
if (textarea) {
sqlCursorPos.value = { start: textarea.selectionStart, end: textarea.selectionEnd }
}
}
const insertParamVar = (index: number) => {
const variable = `\${参数${index + 1}}`
const current = formData.value.sqlScript || ''
const { start, end } = sqlCursorPos.value
const before = current.slice(0, start)
const after = current.slice(end)
formData.value.sqlScript = before + variable + after
selectedParamIndex.value = null
nextTick(() => {
sqlCursorPos.value = { start: start + variable.length, end: start + variable.length }
const textarea = sqlScriptInputRef.value?.$el?.querySelector('textarea')
if (textarea) {
textarea.focus()
textarea.setSelectionRange(start + variable.length, start + variable.length)
}
})
}
const sqlSegments = computed(() => {
const sql = formData.value.sqlScript || ''
const regex = /\$\{([^}]+)\}/g
const segments: { type: 'text' | 'var'; text: string }[] = []
let lastIndex = 0
let m
while ((m = regex.exec(sql)) !== null) {
if (m.index > lastIndex) {
segments.push({ type: 'text', text: sql.slice(lastIndex, m.index) })
}
segments.push({ type: 'var', text: m[0] })
lastIndex = regex.lastIndex
}
if (lastIndex < sql.length) {
segments.push({ type: 'text', text: sql.slice(lastIndex) })
}
return segments
})
const validateSqlParams = () => {
if (formData.value.useParams !== 1) return true
const sql = formData.value.sqlScript || ''
const maxParam = paramList.value.length
const regex = /\$\{参数(\d+)\}/g
let m
const invalid: string[] = []
while ((m = regex.exec(sql)) !== null) {
const n = parseInt(m[1], 10)
if (n < 1 || n > maxParam) {
invalid.push(m[0])
}
}
if (invalid.length > 0) {
message.error(`SQL 中的参数变量 ${invalid.join('、')} 超出参数列表范围参数1~参数${maxParam}`)
return false
}
return true
}
const presetColors = [
'#1890ff', '#52c41a', '#faad14', '#f5222d',
'#722ed1', '#eb2f96', '#13c2c2', '#fa8c16'
]
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
if (type === 'create') {
formData.value.type = props.tagType
}
if (id) {
formLoading.value = true
try {
const data = await CustomTagApi.getCustomTag(id)
formData.value = {
...data,
useParams: data.useParams === true || data.useParams === 1 ? 1 : 0
}
} finally {
formLoading.value = false
}
}
}
defineExpose({ open })
const emit = defineEmits(['success'])
const submitForm = async () => {
await formRef.value.validate()
if (!validateSqlParams()) return
formLoading.value = true
try {
const data = formData.value as CustomTag
if (formType.value === 'create') {
await CustomTagApi.createCustomTag(data)
message.success(t('common.createSuccess'))
} else {
await CustomTagApi.updateCustomTag(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
emit('success')
} finally {
formLoading.value = false
}
}
const resetForm = () => {
formData.value = {
id: undefined,
type: props.tagType,
name: undefined,
expression: undefined,
color: undefined,
sqlScript: undefined,
useParams: 0,
params: undefined
}
formRef.value?.resetFields()
}
</script>
<style scoped>
.form-tip { font-size: 12px; color: var(--el-text-color-secondary); line-height: 1.4; margin-top: 4px; }
.color-field { width: 100%; }
.color-presets { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.color-chip {
width: 28px; height: 28px; border-radius: 6px; cursor: pointer; border: 2px solid transparent; transition: all 0.2s;
}
.color-chip:hover { transform: scale(1.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); }
.color-chip.active { border-color: var(--el-color-primary); box-shadow: 0 0 0 2px var(--el-color-primary-light-7); }
.color-picker-wrap { display: flex; align-items: center; gap: 12px; }
.color-picker-wrap .color-input { flex: 1; min-width: 120px; }
.sql-script-field { width: 100%; }
.param-insert-bar {
display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 8px;
padding: 8px 12px; background: var(--el-fill-color-light); border-radius: 6px;
}
.param-label { font-size: 13px; color: var(--el-text-color-secondary); margin-right: 4px; }
.param-select { width: 180px; }
.sql-preview {
margin-top: 10px; padding: 10px 12px; background: var(--el-fill-color-lighter);
border-radius: 6px; font-family: monospace; font-size: 13px;
}
.preview-label { display: block; font-size: 12px; color: var(--el-text-color-secondary); margin-bottom: 8px; }
.sql-preview-content { line-height: 1.8; word-break: break-all; }
.sql-var-tag { margin: 0 2px; font-family: monospace; }
.sql-text { white-space: pre-wrap; }
</style>

View File

@@ -0,0 +1,190 @@
<template>
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="90px"
>
<el-form-item label="标签名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入标签名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="公式描述" prop="expression">
<el-input
v-model="queryParams.expression"
placeholder="请输入公式描述"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<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-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['ydoyun:custom-tag:create']">
<Icon icon="ep:plus" /> 新增
</el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading" v-hasPermi="['ydoyun:custom-tag:export']">
<Icon icon="ep:download" /> 导出
</el-button>
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch" v-hasPermi="['ydoyun:custom-tag:delete']">
<Icon icon="ep:delete" /> 批量删除
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleRowCheckboxChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="标签名称" align="center" prop="name" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="公式描述" align="center" prop="expression" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="标签颜色" align="center" prop="color" width="100">
<template #default="scope">
<el-tag v-if="scope.row.color" :color="scope.row.color" effect="dark" size="small">{{ scope.row.color }}</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="代入参数" align="center" prop="useParams" width="90">
<template #default="scope">
<el-tag v-if="scope.row.useParams === true || scope.row.useParams === 1" type="success" size="small"></el-tag>
<el-tag v-else type="info" size="small"></el-tag>
</template>
</el-table-column>
<el-table-column label="参数列表" align="center" prop="params" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180" />
<el-table-column label="操作" align="center" width="140" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['ydoyun:custom-tag:update']">
<Icon icon="ep:edit" /> 编辑
</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['ydoyun:custom-tag:delete']">
<Icon icon="ep: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>
<CustomTagFormWithParams ref="formRef" tag-type="product" @success="getList" />
</template>
<script setup lang="ts">
import { isEmpty } from '@/utils/is'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { CustomTagApi, CustomTag } from '@/api/ydoyun/customtag'
import CustomTagFormWithParams from '@/views/ydoyun/customtag/CustomTagFormWithParams.vue'
defineOptions({ name: 'ProductCustomTag' })
const message = useMessage()
const { t } = useI18n()
const loading = ref(true)
const list = ref<CustomTag[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
type: 'product' as const,
name: undefined,
expression: undefined,
createTime: [] as string[]
})
const queryFormRef = ref()
const exportLoading = ref(false)
const getList = async () => {
loading.value = true
try {
const data = await CustomTagApi.getCustomTagPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value?.resetFields()
queryParams.type = 'product'
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 CustomTagApi.deleteCustomTag(id)
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: CustomTag[]) => {
checkedIds.value = records.map((item) => item.id!).filter(Boolean)
}
const handleDeleteBatch = async () => {
try {
await message.delConfirm()
await CustomTagApi.deleteCustomTagList(checkedIds.value)
checkedIds.value = []
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
const handleExport = async () => {
try {
await message.exportConfirm()
exportLoading.value = true
const data = await CustomTagApi.exportCustomTag(queryParams)
download.excel(data, '产品自定义标签.xls')
} catch {
} finally {
exportLoading.value = false
}
}
onMounted(() => getList())
</script>

View File

@@ -0,0 +1,111 @@
<template>
<div class="head-container">
<el-input
v-model="dbName"
class="mb-20px"
clearable
placeholder="请输入数据源名称"
@keyup.enter="handleSearch"
>
<template #prefix>
<Icon icon="ep:search" />
</template>
</el-input>
<el-button type="primary" @click="handleSearch" style="width: 100%">
<Icon icon="ep:search" />查询
</el-button>
</div>
<div class="head-container">
<el-scrollbar height="calc(100vh - 200px)">
<el-table
v-loading="loading"
:data="databaseList"
highlight-current-row
@current-change="handleDatabaseChange"
style="width: 100%"
>
<el-table-column label="名称" prop="dbName" :show-overflow-tooltip="true" min-width="100" />
<el-table-column label="类型" prop="dbType" width="70">
<template #default="scope">
<el-tag v-if="scope.row.dbType === 'mysql'" type="success" size="small">MySQL</el-tag>
<el-tag v-else-if="scope.row.dbType === 'pgsql'" type="primary" size="small">PG</el-tag>
<el-tag v-else-if="scope.row.dbType === 'oracle'" type="warning" size="small">Oracle</el-tag>
<el-tag v-else-if="scope.row.dbType === 'sqlserver'" type="danger" size="small">SQL</el-tag>
<span v-else>{{ scope.row.dbType }}</span>
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
:small="true"
layout="prev, pager, next"
/>
</el-scrollbar>
</div>
</template>
<script lang="ts" setup>
import { ReportDatabaseApi, ReportDatabase } from '@/api/ydoyun/reportdatabase'
defineOptions({ name: 'DatabaseList' })
const dbName = ref('')
const databaseList = ref<ReportDatabase[]>([])
const loading = ref(false)
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
dbName: undefined,
dbType: undefined
})
const emits = defineEmits(['node-click'])
/** 查询数据源列表 */
const getList = async () => {
loading.value = true
try {
const params = {
...queryParams,
dbName: dbName.value || undefined
}
const data = await ReportDatabaseApi.getReportDatabasePage(params)
databaseList.value = data.list || []
total.value = data.total || 0
} catch (error) {
console.error('查询数据源列表失败:', error)
databaseList.value = []
total.value = 0
} finally {
loading.value = false
}
}
/** 处理搜索 */
const handleSearch = () => {
queryParams.pageNo = 1
getList()
}
/** 处理数据源选择 */
const handleDatabaseChange = (row: ReportDatabase | null) => {
if (row) {
emits('node-click', row)
} else {
emits('node-click', undefined)
}
}
/** 初始化 */
onMounted(async () => {
await getList()
})
defineExpose({
getList
})
</script>

View File

@@ -0,0 +1,190 @@
<template>
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="90px"
>
<el-form-item label="标签名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入标签名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="公式描述" prop="expression">
<el-input
v-model="queryParams.expression"
placeholder="请输入公式描述"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<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-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['ydoyun:custom-tag:create']">
<Icon icon="ep:plus" /> 新增
</el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading" v-hasPermi="['ydoyun:custom-tag:export']">
<Icon icon="ep:download" /> 导出
</el-button>
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch" v-hasPermi="['ydoyun:custom-tag:delete']">
<Icon icon="ep:delete" /> 批量删除
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleRowCheckboxChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="标签名称" align="center" prop="name" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="公式描述" align="center" prop="expression" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="标签颜色" align="center" prop="color" width="100">
<template #default="scope">
<el-tag v-if="scope.row.color" :color="scope.row.color" effect="dark" size="small">{{ scope.row.color }}</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="代入参数" align="center" prop="useParams" width="90">
<template #default="scope">
<el-tag v-if="scope.row.useParams === true || scope.row.useParams === 1" type="success" size="small"></el-tag>
<el-tag v-else type="info" size="small"></el-tag>
</template>
</el-table-column>
<el-table-column label="参数列表" align="center" prop="params" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180" />
<el-table-column label="操作" align="center" width="140" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['ydoyun:custom-tag:update']">
<Icon icon="ep:edit" /> 编辑
</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['ydoyun:custom-tag:delete']">
<Icon icon="ep: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>
<CustomTagFormWithParams ref="formRef" tag-type="store" @success="getList" />
</template>
<script setup lang="ts">
import { isEmpty } from '@/utils/is'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { CustomTagApi, CustomTag } from '@/api/ydoyun/customtag'
import CustomTagFormWithParams from '@/views/ydoyun/customtag/CustomTagFormWithParams.vue'
defineOptions({ name: 'StoreCustomTag' })
const message = useMessage()
const { t } = useI18n()
const loading = ref(true)
const list = ref<CustomTag[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
type: 'store' as const,
name: undefined,
expression: undefined,
createTime: [] as string[]
})
const queryFormRef = ref()
const exportLoading = ref(false)
const getList = async () => {
loading.value = true
try {
const data = await CustomTagApi.getCustomTagPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value?.resetFields()
queryParams.type = 'store'
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 CustomTagApi.deleteCustomTag(id)
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: CustomTag[]) => {
checkedIds.value = records.map((item) => item.id!).filter(Boolean)
}
const handleDeleteBatch = async () => {
try {
await message.delConfirm()
await CustomTagApi.deleteCustomTagList(checkedIds.value)
checkedIds.value = []
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
const handleExport = async () => {
try {
await message.exportConfirm()
exportLoading.value = true
const data = await CustomTagApi.exportCustomTag(queryParams)
download.excel(data, '店铺自定义标签.xls')
} catch {
} finally {
exportLoading.value = false
}
}
onMounted(() => getList())
</script>

View File

@@ -0,0 +1,190 @@
<template>
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="90px"
>
<el-form-item label="标签名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入标签名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="公式描述" prop="expression">
<el-input
v-model="queryParams.expression"
placeholder="请输入公式描述"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<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-220px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" />搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" />重置</el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['ydoyun:custom-tag:create']">
<Icon icon="ep:plus" /> 新增
</el-button>
<el-button type="success" plain @click="handleExport" :loading="exportLoading" v-hasPermi="['ydoyun:custom-tag:export']">
<Icon icon="ep:download" /> 导出
</el-button>
<el-button type="danger" plain :disabled="isEmpty(checkedIds)" @click="handleDeleteBatch" v-hasPermi="['ydoyun:custom-tag:delete']">
<Icon icon="ep:delete" /> 批量删除
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table
row-key="id"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
@selection-change="handleRowCheckboxChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="标签名称" align="center" prop="name" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="公式描述" align="center" prop="expression" min-width="150" :show-overflow-tooltip="true" />
<el-table-column label="标签颜色" align="center" prop="color" width="100">
<template #default="scope">
<el-tag v-if="scope.row.color" :color="scope.row.color" effect="dark" size="small">{{ scope.row.color }}</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="代入参数" align="center" prop="useParams" width="90">
<template #default="scope">
<el-tag v-if="scope.row.useParams === true || scope.row.useParams === 1" type="success" size="small"></el-tag>
<el-tag v-else type="info" size="small"></el-tag>
</template>
</el-table-column>
<el-table-column label="参数列表" align="center" prop="params" min-width="120" :show-overflow-tooltip="true" />
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180" />
<el-table-column label="操作" align="center" width="140" fixed="right">
<template #default="scope">
<el-button link type="primary" @click="openForm('update', scope.row.id)" v-hasPermi="['ydoyun:custom-tag:update']">
<Icon icon="ep:edit" /> 编辑
</el-button>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['ydoyun:custom-tag:delete']">
<Icon icon="ep: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>
<CustomTagFormWithParams ref="formRef" tag-type="supplier" @success="getList" />
</template>
<script setup lang="ts">
import { isEmpty } from '@/utils/is'
import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import { CustomTagApi, CustomTag } from '@/api/ydoyun/customtag'
import CustomTagFormWithParams from '@/views/ydoyun/customtag/CustomTagFormWithParams.vue'
defineOptions({ name: 'SupplierCustomTag' })
const message = useMessage()
const { t } = useI18n()
const loading = ref(true)
const list = ref<CustomTag[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
type: 'supplier' as const,
name: undefined,
expression: undefined,
createTime: [] as string[]
})
const queryFormRef = ref()
const exportLoading = ref(false)
const getList = async () => {
loading.value = true
try {
const data = await CustomTagApi.getCustomTagPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value?.resetFields()
queryParams.type = 'supplier'
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 CustomTagApi.deleteCustomTag(id)
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: CustomTag[]) => {
checkedIds.value = records.map((item) => item.id!).filter(Boolean)
}
const handleDeleteBatch = async () => {
try {
await message.delConfirm()
await CustomTagApi.deleteCustomTagList(checkedIds.value)
checkedIds.value = []
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
const handleExport = async () => {
try {
await message.exportConfirm()
exportLoading.value = true
const data = await CustomTagApi.exportCustomTag(queryParams)
download.excel(data, '供货商自定义标签.xls')
} catch {
} finally {
exportLoading.value = false
}
}
onMounted(() => getList())
</script>