fix: 自定义标签
This commit is contained in:
311
src/views/ydoyun/customtag/CustomTagFormWithParams.vue
Normal file
311
src/views/ydoyun/customtag/CustomTagFormWithParams.vue
Normal 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>
|
||||
190
src/views/ydoyun/productcustomtag/index.vue
Normal file
190
src/views/ydoyun/productcustomtag/index.vue
Normal 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>
|
||||
111
src/views/ydoyun/reportdatabase/DatabaseList.vue
Normal file
111
src/views/ydoyun/reportdatabase/DatabaseList.vue
Normal 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>
|
||||
190
src/views/ydoyun/storecustomtag/index.vue
Normal file
190
src/views/ydoyun/storecustomtag/index.vue
Normal 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>
|
||||
190
src/views/ydoyun/suppliercustomtag/index.vue
Normal file
190
src/views/ydoyun/suppliercustomtag/index.vue
Normal 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>
|
||||
Reference in New Issue
Block a user