1. 提交代码(标签同步)
This commit is contained in:
@@ -832,7 +832,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
component: () => import('@/views/ydoyun/report/lijun/reportpage6/detail.vue')
|
||||
},
|
||||
{
|
||||
path: 'wbl/product-splb',
|
||||
path: 'wangbuliao/product-splb',
|
||||
name: 'ProductSplbReport',
|
||||
meta: {
|
||||
title: '商品列表报表',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<Dialog :title="'编辑SQL - ' + (tag?.name || '')" v-model="dialogVisible" width="800px" max-height="85vh">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
|
||||
<el-form :model="form" label-width="100px">
|
||||
<el-form-item label="标签名称">
|
||||
<el-input :model-value="tag?.name" readonly />
|
||||
</el-form-item>
|
||||
@@ -28,14 +28,26 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="标签同步">
|
||||
<div class="sync-switch-row">
|
||||
<el-switch
|
||||
:model-value="syncEnabled"
|
||||
active-text="开启同步"
|
||||
inactive-text="关闭同步"
|
||||
@update:model-value="onSyncEnabledChange"
|
||||
/>
|
||||
<span class="form-item-hint sync-switch-hint">关闭后本标签不执行 SQL 同步;确定保存后脚本将清空。再次开启可继续编辑或恢复关前内容。</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="执行SQL脚本" prop="sqlScript">
|
||||
<div class="param-ref-row">
|
||||
<el-button size="small" type="success" plain @click="insertInsertTemplate">
|
||||
<el-button size="small" type="success" plain :disabled="!syncEnabled" @click="insertInsertTemplate">
|
||||
插入 {{ insertTableName }} 模板
|
||||
</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
class="btn-ref-color"
|
||||
:disabled="!syncEnabled"
|
||||
:style="{ backgroundColor: editableColor || '#409EFF', borderColor: editableColor || '#409EFF', color: '#fff' }"
|
||||
@click="insertParamRef('颜色')"
|
||||
>
|
||||
@@ -49,6 +61,7 @@
|
||||
size="small"
|
||||
type="info"
|
||||
plain
|
||||
:disabled="!syncEnabled"
|
||||
@click="insertParamRef(p)"
|
||||
>
|
||||
引用{{ p }}
|
||||
@@ -60,12 +73,15 @@
|
||||
v-model="form.sqlScript"
|
||||
type="textarea"
|
||||
:rows="10"
|
||||
placeholder="请输入标签同步时执行的SQL脚本,支持多行。可使用上方按钮插入参数引用,格式为 ${参数1}、${参数2} 等"
|
||||
:disabled="!syncEnabled"
|
||||
placeholder="开启「标签同步」后可编辑。支持 ${参数1}、${颜色} 等占位符"
|
||||
class="sql-script-area"
|
||||
/>
|
||||
<div class="form-item-hint">该SQL将用于标签计算与同步,请确保语法正确。修改后需点击底部保存按钮统一保存。</div>
|
||||
<div class="form-item-hint">
|
||||
与上方「标签同步」开关配合:关=不同步,开=可填写脚本。点「确定」后请在配置页点「保存」统一提交。
|
||||
</div>
|
||||
<!-- SQL 预览:文本框下方,参数代入并高亮 -->
|
||||
<div class="sql-preview-block" v-if="form.sqlScript && paramList.length > 0">
|
||||
<div class="sql-preview-block" v-if="syncEnabled && form.sqlScript && paramList.length > 0">
|
||||
<div class="sql-preview-label">SQL 预览(参数已代入):</div>
|
||||
<div class="sql-preview-content" v-html="previewHtml"></div>
|
||||
</div>
|
||||
@@ -80,6 +96,7 @@
|
||||
size="small"
|
||||
placeholder="输入值"
|
||||
class="preview-param-input"
|
||||
:disabled="!syncEnabled"
|
||||
@input="updatePreview"
|
||||
/>
|
||||
</div>
|
||||
@@ -119,21 +136,32 @@ const COLOR_OPTIONS = [
|
||||
]
|
||||
|
||||
const form = ref({ sqlScript: '' })
|
||||
const formRef = ref()
|
||||
/** 关同步前暂存的脚本,仅再次开启同一次弹窗时恢复 */
|
||||
const sqlStash = ref('')
|
||||
/** 与 SQL 是否为空一致:有脚本=开启;关闭开关会清空编辑区并暂存到 sqlStash */
|
||||
const syncEnabled = ref(true)
|
||||
const sqlTextareaRef = ref()
|
||||
const saving = ref(false)
|
||||
const editableColor = ref('')
|
||||
const previewParamValues = ref<Record<string, string>>({})
|
||||
const previewHtml = ref('')
|
||||
const rules = {
|
||||
sqlScript: [{ required: true, message: '执行SQL脚本不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
/** 颜色以外的参数列表(颜色由上方标签颜色选择器编辑) */
|
||||
const paramListWithoutColor = computed(() =>
|
||||
paramList.value.filter((p) => p !== '颜色')
|
||||
)
|
||||
|
||||
const onSyncEnabledChange = (val: boolean) => {
|
||||
if (!val) {
|
||||
sqlStash.value = form.value.sqlScript || ''
|
||||
form.value.sqlScript = ''
|
||||
} else {
|
||||
form.value.sqlScript = sqlStash.value
|
||||
}
|
||||
syncEnabled.value = val
|
||||
nextTick(updatePreview)
|
||||
}
|
||||
|
||||
const setEditableColor = (color: string) => {
|
||||
editableColor.value = color
|
||||
previewParamValues.value = { ...previewParamValues.value, '颜色': color }
|
||||
@@ -196,7 +224,10 @@ const updatePreview = () => {
|
||||
watch(
|
||||
() => tag.value,
|
||||
(t) => {
|
||||
form.value.sqlScript = t?.sqlScript || ''
|
||||
const raw = t?.sqlScript || ''
|
||||
form.value.sqlScript = raw
|
||||
sqlStash.value = raw
|
||||
syncEnabled.value = raw.trim() !== ''
|
||||
const defaults = (t as any)?.paramDefaults ?? {}
|
||||
const init: Record<string, string> = {}
|
||||
paramList.value.forEach((p) => {
|
||||
@@ -220,6 +251,7 @@ watch(
|
||||
)
|
||||
|
||||
const insertAtCursor = (text: string) => {
|
||||
if (!syncEnabled.value) return
|
||||
const textarea = sqlTextareaRef.value?.$el?.querySelector('textarea')
|
||||
if (textarea) {
|
||||
const start = textarea.selectionStart
|
||||
@@ -237,6 +269,7 @@ const insertAtCursor = (text: string) => {
|
||||
}
|
||||
|
||||
const insertInsertTemplate = () => {
|
||||
if (!syncEnabled.value) return
|
||||
const table = insertTableName.value
|
||||
const template = `INSERT INTO ${table} (code, name, color)
|
||||
SELECT code, name, '\${颜色}' AS color FROM (
|
||||
@@ -246,6 +279,7 @@ SELECT code, name, '\${颜色}' AS color FROM (
|
||||
`
|
||||
const oldScript = form.value.sqlScript || ''
|
||||
form.value.sqlScript = template + (oldScript ? '\n' + oldScript : '')
|
||||
sqlStash.value = form.value.sqlScript
|
||||
// 颜色参数默认取标签当前颜色
|
||||
const colorVal = tag.value?.color ?? '#409EFF'
|
||||
editableColor.value = colorVal
|
||||
@@ -261,6 +295,7 @@ SELECT code, name, '\${颜色}' AS color FROM (
|
||||
}
|
||||
|
||||
const insertParamRef = (paramName: string) => {
|
||||
if (!syncEnabled.value) return
|
||||
insertAtCursor(`\${${paramName}}`)
|
||||
}
|
||||
|
||||
@@ -269,17 +304,15 @@ const substituteParams = (sql: string, values: Record<string, string>) => {
|
||||
}
|
||||
|
||||
const handleConfirm = async () => {
|
||||
await formRef.value?.validate()
|
||||
if (!tag.value?.id) return
|
||||
saving.value = true
|
||||
try {
|
||||
const sqlScript = form.value.sqlScript
|
||||
const sqlScript = syncEnabled.value ? (form.value.sqlScript?.trim() ?? '') : ''
|
||||
const paramVals = { ...previewParamValues.value }
|
||||
// 始终带上标签颜色,供父组件更新 tagColorValues 及保存
|
||||
// 始终带上标签颜色,供父组件更新 tagColorValues 及保存(含仅含 ${颜色} 无其它参数的情况)
|
||||
if (editableColor.value) paramVals['颜色'] = editableColor.value
|
||||
const sqlScriptResolved = paramList.value.length > 0
|
||||
? substituteParams(sqlScript, paramVals)
|
||||
: sqlScript
|
||||
// 只要脚本里出现 ${...} 占位符就替换,不依赖 paramList 长度(避免仅「颜色」或解析时序问题导致不替换)
|
||||
const sqlScriptResolved = substituteParams(sqlScript, paramVals)
|
||||
emit('confirm', tag.value.id, sqlScript, sqlScriptResolved, paramVals)
|
||||
dialogVisible.value = false
|
||||
} finally {
|
||||
@@ -315,6 +348,18 @@ const handleConfirm = async () => {
|
||||
font-family: 'Monaco', 'Menlo', 'Consolas', monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
.sync-switch-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 6px;
|
||||
}
|
||||
.sync-switch-hint {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
max-width: 100%;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.form-item-hint {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
placeholder="请输入标签同步时执行的SQL脚本,支持多行"
|
||||
class="sql-script-area"
|
||||
/>
|
||||
<div class="form-item-hint">该SQL将用于标签计算与同步,请确保语法正确</div>
|
||||
<div class="form-item-hint">可为空:留空表示关闭该标签同步;有内容时请确保语法正确</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否代入参数" prop="useParams">
|
||||
<el-radio-group v-model="formData.useParams">
|
||||
@@ -90,7 +90,6 @@ const formData = ref<Tag>({
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '标签名称不能为空', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '请选择标签类型', trigger: 'change' }],
|
||||
sqlScript: [{ required: true, message: '执行SQL脚本不能为空', trigger: 'blur' }],
|
||||
useParams: [{ required: true, message: '请选择是否代入参数', trigger: 'change' }],
|
||||
})
|
||||
const formRef = ref()
|
||||
|
||||
@@ -1423,6 +1423,64 @@ const getTagParamListAndValues = (type: string, name: string) => {
|
||||
return { paramList, values }
|
||||
}
|
||||
|
||||
const SQL_PLACEHOLDER_NAMES = (sql: string): string[] => {
|
||||
if (!sql) return []
|
||||
const s = new Set<string>()
|
||||
const re = /\$\{([^}]+)\}/g
|
||||
let m: RegExpExecArray | null
|
||||
while ((m = re.exec(sql)) !== null) s.add(m[1].trim())
|
||||
return [...s]
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存时构造「占位符名 → 值」:不依赖是否打开过编辑 SQL 弹窗。
|
||||
* 顺序:预设参数默认值 < 标签接口 paramValues < 主页面已编辑的 tagParamValues;颜色与 getTagColor 一致。
|
||||
* 再按当前 sqlScript 中出现的 ${...} 补全仍为空的项目(如仅含 ${颜色}、首次进入未点过主页面输入框)。
|
||||
*/
|
||||
const collectTagParamValuesForSave = (type: string, name: string, t: Tag, sqlScript: string): Record<string, string> => {
|
||||
const key = type && name ? `${type}:${name}` : ''
|
||||
const preset = key ? PRESET_TAGS.find((p) => p.type === type && p.name === name) : undefined
|
||||
const { values: presetVals } = getTagParamListAndValues(type, name)
|
||||
let fromApi: Record<string, string> = {}
|
||||
if (t.paramValues) {
|
||||
try {
|
||||
const v = JSON.parse(t.paramValues) as unknown
|
||||
if (v && typeof v === 'object' && !Array.isArray(v)) fromApi = { ...v } as Record<string, string>
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
const fromPanel = key && tagParamValues[key] ? { ...tagParamValues[key] } : {}
|
||||
const merged: Record<string, string> = { ...presetVals, ...fromApi, ...fromPanel }
|
||||
|
||||
const resolveColor = (): string => {
|
||||
if (!key) return t.color || preset?.color || '#409EFF'
|
||||
return (
|
||||
tagColorValues[key] ||
|
||||
fromPanel['颜色'] ||
|
||||
fromApi['颜色'] ||
|
||||
t.color ||
|
||||
preset?.color ||
|
||||
'#409EFF'
|
||||
)
|
||||
}
|
||||
|
||||
const names = SQL_PLACEHOLDER_NAMES(sqlScript)
|
||||
for (const pname of names) {
|
||||
if (pname === '颜色') {
|
||||
merged['颜色'] = resolveColor()
|
||||
continue
|
||||
}
|
||||
if (merged[pname] != null && String(merged[pname]) !== '') continue
|
||||
const m = /^参数(\d+)$/.exec(pname)
|
||||
if (m) {
|
||||
const idx = parseInt(m[1], 10) - 1
|
||||
if (idx >= 0) merged[pname] = getTagParamValue(type, name, idx)
|
||||
}
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
const activeTabList = computed(() => {
|
||||
const tab = tabList.find((t) => t.name === activeTab.value)
|
||||
return tab ? [tab] : []
|
||||
@@ -1583,16 +1641,15 @@ const saveConfig = async () => {
|
||||
const paramDef = tagKey ? PRESET_TAG_PARAMS[tagKey] : undefined
|
||||
const useParams = t.useParams ?? preset?.useParams ?? false
|
||||
const params = (t.params || preset?.params || paramDef?.params || '').trim()
|
||||
const { values: paramVals } = getTagParamListAndValues(t.type || '', t.name || '')
|
||||
const sqlScript = edit?.sqlScript ?? t.sqlScript ?? ''
|
||||
const sqlScriptResolved =
|
||||
edit?.sqlScriptResolved ??
|
||||
(paramVals && Object.keys(paramVals).length && sqlScript
|
||||
? substituteParams(sqlScript, paramVals)
|
||||
: t.sqlScriptResolved ?? sqlScript)
|
||||
const mergedParamVals = collectTagParamValuesForSave(t.type || '', t.name || '', t, sqlScript)
|
||||
// 保存时按当前 sql、接口/主页面参数与颜色重算(未打开过编辑 SQL 弹窗同样生效)
|
||||
const sqlScriptResolved = sqlScript.trim()
|
||||
? substituteParams(sqlScript, mergedParamVals)
|
||||
: ''
|
||||
const color = tagColorValues[tagKey] ?? t.color ?? preset?.color
|
||||
const paramValuesStr =
|
||||
paramVals && Object.keys(paramVals).length > 0 ? JSON.stringify(paramVals) : undefined
|
||||
Object.keys(mergedParamVals).length > 0 ? JSON.stringify(mergedParamVals) : undefined
|
||||
return {
|
||||
...t,
|
||||
sqlScript,
|
||||
|
||||
Reference in New Issue
Block a user