fix: 三平台内容差异化 + admin敏感词管理表格化
- writer.py: _expand_section() 去除 <100字阈值,始终调用 LLM 平台专属扩写
- prompt_loader.py: 新增 section_expansion_zhihu/wechat/xiaohongshu 三个独立 prompt
- admin.html: 配置管理标签页 + 敏感词/清理规则子标签 + 敏感词表格化管理(编辑/删除)
- config_items.py: PUT /sensitive-words/{id} 支持更新 word/category
- compliance_checker.py: AI 套话从 DB 加载 + 人称规则修正
- initial_data.py: PlatformConfig 字数迁移 + 新种子
- 各前端页面: LLM 配置 rate_limit 字段 + 供应商列表排序
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<script src="uni-nav.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="app" v-cloak>
|
||||
<uni-nav title="系统管理" :username="currentUser.username" :is-admin="isAdmin" current-page="admin" @navigate="redirectToPage" @logout="logout">
|
||||
</uni-nav>
|
||||
<div class="main-content">
|
||||
@@ -32,6 +32,7 @@
|
||||
<el-button size="default" :type="activeTab === 'logs' ? 'primary' : ''" @click="switchTab('logs')">运行日志</el-button>
|
||||
<el-button size="default" :type="activeTab === 'assistant' ? 'primary' : ''" @click="switchTab('assistant')">AI 助手</el-button>
|
||||
<el-button size="default" :type="activeTab === 'searchproviders' ? 'primary' : ''" @click="switchTab('searchproviders')">搜索API</el-button>
|
||||
<el-button size="default" :type="activeTab === 'configitems' ? 'primary' : ''" @click="switchTab('configitems')">配置管理</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -135,8 +136,11 @@
|
||||
<template #default="scope">{{ scope.row.base_url ? scope.row.base_url.replace(/^https?:\/\//, '') : '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="temperature" label="温度" width="70"></el-table-column>
|
||||
<el-table-column prop="max_tokens" label="最大Token" width="100"></el-table-column>
|
||||
<el-table-column prop="is_active" label="状态" width="80">
|
||||
<el-table-column prop="max_tokens" label="最大Token" width="100"></el-table-column>
|
||||
<el-table-column label="调用上限" width="110">
|
||||
<template #default="scope">{{ scope.row.rate_limit ? scope.row.rate_limit + '次/' + (scope.row.rate_limit_window_minutes || 300) + '分' : '不限' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="is_active" label="状态" width="80">
|
||||
<template #default="scope">
|
||||
<el-tag v-if="scope.row.is_active" type="success" size="small" effect="dark">启用</el-tag>
|
||||
<el-tag v-else type="info" size="small">停用</el-tag>
|
||||
@@ -164,6 +168,7 @@
|
||||
<div class="card-row"><span class="card-label">供应商</span><span class="card-value">{{ item.provider }}</span></div>
|
||||
<div class="card-row"><span class="card-label">模型</span><span class="card-value">{{ item.model }}</span></div>
|
||||
<div class="card-row"><span class="card-label">温度/最大Token</span><span class="card-value">{{ item.temperature }} / {{ item.max_tokens }}</span></div>
|
||||
<div class="card-row"><span class="card-label">调用上限</span><span class="card-value">{{ item.rate_limit ? item.rate_limit + '次/' + (item.rate_limit_window_minutes || 300) + '分' : '不限' }}</span></div>
|
||||
<div class="card-row"><span class="card-label">状态</span><span class="card-value"><el-tag v-if="item.is_active" type="success" size="small" effect="dark">当前使用</el-tag><el-tag v-else type="info" size="small">未启用</el-tag></span></div>
|
||||
<div class="card-actions">
|
||||
<el-button size="small" @click="showLLMConfigDialog(item)">编辑</el-button>
|
||||
@@ -601,19 +606,160 @@
|
||||
</div>
|
||||
<template #footer><el-button @click="searchProviderTestVisible = false">关闭</el-button></template>
|
||||
</el-dialog>
|
||||
|
||||
<div v-if="activeTab === 'configitems'">
|
||||
<div class="filter-bar">
|
||||
<el-button size="default" :type="configSubTab === 'sensitive' ? 'primary' : ''" @click="switchConfigSubTab('sensitive')">🔒 敏感词</el-button>
|
||||
<el-button size="default" :type="configSubTab === 'cleanrules' ? 'primary' : ''" @click="switchConfigSubTab('cleanrules')">🧹 清理规则</el-button>
|
||||
</div>
|
||||
|
||||
<template v-if="configSubTab === 'sensitive'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showAddSensitiveWord">新增敏感词</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ sensitiveWords.length }} 个</span>
|
||||
</div>
|
||||
<div v-if="swLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="sensitiveWords.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconWarning /></el-icon>
|
||||
<div class="empty-text">暂无敏感词</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="sensitiveWords" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column type="index" label="#" width="50"></el-table-column>
|
||||
<el-table-column prop="word" label="敏感词" min-width="160"></el-table-column>
|
||||
<el-table-column prop="category" label="类别" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small">{{ row.category || '未分类' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="editSensitiveWord(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteSensitiveWord(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="card-list-mobile">
|
||||
<div v-for="item in sensitiveWords" :key="item.id" class="card-item">
|
||||
<div><el-tag size="small">{{ item.category || '未分类' }}</el-tag> <strong>{{ item.word }}</strong></div>
|
||||
<div style="margin-top:4px;">
|
||||
<el-button size="small" @click="editSensitiveWord(item)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteSensitiveWord(item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-if="configSubTab === 'cleanrules'">
|
||||
<div class="toolbar">
|
||||
<el-button type="primary" size="small" @click="showAddCleanRule">新增清理规则</el-button>
|
||||
<span style="font-size:13px;color:#909399;margin-left:8px;">共 {{ contentCleanRules.length }} 条</span>
|
||||
</div>
|
||||
<div v-if="ccrLoading" class="card-loading">加载中...</div>
|
||||
<template v-else-if="contentCleanRules.length === 0">
|
||||
<div class="empty-state">
|
||||
<el-icon style="font-size:48px;color:#c0c4cc;"><IconWarning /></el-icon>
|
||||
<div class="empty-text">暂无清理规则</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-table :data="contentCleanRules" border stripe class="data-table" style="width:100%">
|
||||
<el-table-column type="index" label="#" width="50"></el-table-column>
|
||||
<el-table-column prop="rule_type" label="类型" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag size="small">{{ row.rule_type }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="description" label="描述" min-width="140"></el-table-column>
|
||||
<el-table-column prop="pattern" label="模式" min-width="220">
|
||||
<template #default="{ row }"><code style="font-size:12px;word-break:break-all;">{{ row.pattern }}</code></template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="80">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.is_active ? 'success' : 'info'" size="small">{{ row.is_active ? '启用' : '停用' }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button size="small" @click="editCleanRule(row)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteCleanRule(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="card-list-mobile">
|
||||
<div v-for="item in contentCleanRules" :key="item.id" class="card-item">
|
||||
<div><el-tag size="small">{{ item.rule_type }}</el-tag> <code style="font-size:12px;">{{ item.pattern }}</code></div>
|
||||
<div style="margin-top:4px;">{{ item.description }}</div>
|
||||
<div style="margin-top:4px;">
|
||||
<el-tag :type="item.is_active ? 'success' : 'info'" size="small">{{ item.is_active ? '启用' : '停用' }}</el-tag>
|
||||
<el-button size="small" style="margin-left:8px;" @click="editCleanRule(item)">编辑</el-button>
|
||||
<el-button size="small" type="danger" @click="deleteCleanRule(item)">删除</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="swDialogVisible" :title="editingSwId ? '编辑敏感词' : '新增敏感词'" width="450px" :close-on-click-modal="false">
|
||||
<el-form :model="swForm" label-width="80px">
|
||||
<el-form-item label="敏感词"><el-input v-model="swForm.word" placeholder="输入敏感词"/></el-form-item>
|
||||
<el-form-item label="类别">
|
||||
<el-select v-model="swForm.category" style="width:100%">
|
||||
<el-option label="general" value="general" />
|
||||
<el-option label="政治敏感" value="政治敏感" />
|
||||
<el-option label="违禁内容" value="违禁内容" />
|
||||
<el-option label="不实信息" value="不实信息" />
|
||||
<el-option label="porn" value="porn" />
|
||||
<el-option label="ad" value="ad" />
|
||||
<el-option label="violence" value="violence" />
|
||||
<el-option label="other" value="other" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="swDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveSensitiveWord" :loading="swSaving">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="ccrDialogVisible" :title="ccrDialogTitle" width="650px" :close-on-click-modal="false">
|
||||
<el-form :model="ccrForm" label-width="100px">
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="ccrForm.rule_type" style="width:100%">
|
||||
<el-option label="thinking (AI思考)" value="thinking" />
|
||||
<el-option label="preface (AI前缀)" value="preface" />
|
||||
<el-option label="verbosity (AI废话)" value="verbosity" />
|
||||
<el-option label="ai_telltale (AI套话)" value="ai_telltale" />
|
||||
<el-option label="html_thinking" value="html_thinking" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="模式"><el-input v-model="ccrForm.pattern" placeholder="正则表达式"/></el-form-item>
|
||||
<el-form-item label="描述"><el-input v-model="ccrForm.description" placeholder="简短说明"/></el-form-item>
|
||||
<el-form-item label="排序"><el-input-number v-model="ccrForm.sort_order" :min="0" :max="999" style="width:100%"/></el-form-item>
|
||||
<el-form-item label="启用"><el-switch v-model="ccrForm.is_active"/></el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="ccrDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="saveCleanRule" :loading="ccrSaving">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="llmConfigDialogVisible" :title="llmConfigDialogTitle" width="750px" :close-on-click-modal="false">
|
||||
<el-form :model="llmConfigForm" label-width="110px">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="名称"><el-input v-model="llmConfigForm.name" placeholder="如:default_expand"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="供应商">
|
||||
<el-select v-model="llmConfigForm.provider" style="width:100%">
|
||||
<el-option label="opencode-go (deepseek-v4-flash)" value="opencode-go"></el-option>
|
||||
<el-option label="nvidia (step-3.7-flash)" value="nvidia"></el-option>
|
||||
<el-option label="sensenova (deepseek-v4-flash)" value="sensenova"></el-option>
|
||||
<el-option label="sensenova" value="sensenova"></el-option>
|
||||
<el-option label="nvidia" value="nvidia"></el-option>
|
||||
<el-option label="opencode-go" value="opencode-go"></el-option>
|
||||
</el-select>
|
||||
</el-form-item></el-col>
|
||||
</el-row>
|
||||
@@ -630,6 +776,10 @@
|
||||
<el-col :span="6"><el-form-item label="激活"><el-switch v-model="llmConfigForm.is_active"/></el-form-item></el-col>
|
||||
<el-col :span="6"><el-form-item label="默认"><el-switch v-model="llmConfigForm.is_default"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="12"><el-form-item label="调用上限"><el-input-number v-model="llmConfigForm.rate_limit" :min="0" :max="999999" style="width:100%"/></el-form-item></el-col>
|
||||
<el-col :span="12"><el-form-item label="时间窗口(分)"><el-input-number v-model="llmConfigForm.rate_limit_window_minutes" :min="1" :max="43200" style="width:100%"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="24"><el-form-item label="系统提示词"><el-input type="textarea" v-model="llmConfigForm.system_prompt" :rows="3" placeholder="你是一个专业的内容创作助手。"/></el-form-item></el-col>
|
||||
</el-row>
|
||||
@@ -692,6 +842,7 @@
|
||||
};
|
||||
|
||||
const activeTab = ref('users');
|
||||
const configSubTab = ref('sensitive');
|
||||
const currentUser = ref({ username: '' });
|
||||
const isAdmin = ref(false);
|
||||
|
||||
@@ -711,7 +862,7 @@ const llmConfigs = ref([]);
|
||||
const llmConfigsLoading = ref(false);
|
||||
const llmConfigDialogVisible = ref(false);
|
||||
const llmConfigDialogTitle = ref('新增 LLM 配置');
|
||||
const llmConfigForm = reactive({ id: null, name: '', system_prompt: '', user_prompt_template: '', temperature: 0.7, max_tokens: 131072, model: '', provider: 'opencode-go', base_url: '', api_key: '', is_active: true, is_default: false });
|
||||
const llmConfigForm = reactive({ id: null, name: '', system_prompt: '', user_prompt_template: '', temperature: 0.7, max_tokens: 131072, model: '', provider: 'opencode-go', base_url: '', api_key: '', is_active: true, is_default: false, rate_limit: 0, rate_limit_window_minutes: 300 });
|
||||
const editingLLMConfigId = ref(null);
|
||||
const loadLLMConfigs = async () => {
|
||||
llmConfigsLoading.value = true;
|
||||
@@ -727,6 +878,7 @@ const llmConfigs = ref([]);
|
||||
llmConfigForm.id = null; llmConfigForm.name = ''; llmConfigForm.provider = 'sensenova'; llmConfigForm.model = 'deepseek-v4-flash';
|
||||
llmConfigForm.base_url = 'https://token.sensenova.cn/v1'; llmConfigForm.api_key = ''; llmConfigForm.temperature = 0.3;
|
||||
llmConfigForm.max_tokens = 4000; llmConfigForm.system_prompt = ''; llmConfigForm.user_prompt_template = ''; llmConfigForm.is_active = true; llmConfigForm.is_default = false;
|
||||
llmConfigForm.rate_limit = 0; llmConfigForm.rate_limit_window_minutes = 300;
|
||||
}
|
||||
llmConfigDialogVisible.value = true;
|
||||
};
|
||||
@@ -1078,6 +1230,87 @@ const llmConfigs = ref([]);
|
||||
catch (e) { ElMessage.error('重置失败: ' + e.message); }
|
||||
};
|
||||
|
||||
const sensitiveWords = ref([]);
|
||||
const swLoading = ref(false);
|
||||
const swDialogVisible = ref(false);
|
||||
const swSaving = ref(false);
|
||||
const swForm = reactive({ word: '', category: 'general' });
|
||||
const editingSwId = ref(null);
|
||||
const loadSensitiveWords = async () => {
|
||||
swLoading.value = true;
|
||||
try { sensitiveWords.value = await api.get('/api/admin/config/sensitive-words'); }
|
||||
catch (e) { console.error(e); }
|
||||
finally { swLoading.value = false; }
|
||||
};
|
||||
const showAddSensitiveWord = () => { swForm.word = ''; swForm.category = 'general'; editingSwId.value = null; swDialogVisible.value = true; };
|
||||
const editSensitiveWord = (row) => { swForm.word = row.word; swForm.category = row.category || 'general'; editingSwId.value = row.id; swDialogVisible.value = true; };
|
||||
const saveSensitiveWord = async () => {
|
||||
swSaving.value = true;
|
||||
try {
|
||||
const body = { word: swForm.word, category: swForm.category };
|
||||
if (editingSwId.value) { await api.put('/api/admin/config/sensitive-words/' + editingSwId.value, body); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/admin/config/sensitive-words', body); ElMessage.success('添加成功'); }
|
||||
swDialogVisible.value = false;
|
||||
await loadSensitiveWords();
|
||||
}
|
||||
catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
finally { swSaving.value = false; }
|
||||
};
|
||||
const deleteSensitiveWord = async (row) => {
|
||||
try { await ElMessageBox.confirm('确定删除「' + row.word + '」吗?', '提示', { type: 'warning' }); await api.delete('/api/admin/config/sensitive-words/' + row.id); ElMessage.success('删除成功'); await loadSensitiveWords(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
const sensitiveWordsByCategory = computed(() => {
|
||||
const map = {};
|
||||
for (const w of sensitiveWords.value) {
|
||||
const cat = w.category || '未分类';
|
||||
if (!map[cat]) map[cat] = [];
|
||||
map[cat].push(w);
|
||||
}
|
||||
return Object.entries(map).sort((a, b) => a[0].localeCompare(b[0]));
|
||||
});
|
||||
|
||||
const contentCleanRules = ref([]);
|
||||
const ccrLoading = ref(false);
|
||||
const ccrDialogVisible = ref(false);
|
||||
const ccrDialogTitle = ref('新增清理规则');
|
||||
const ccrSaving = ref(false);
|
||||
const ccrForm = reactive({ id: null, rule_type: 'ai_telltale', pattern: '', description: '', sort_order: 0, is_active: true });
|
||||
const editingCcrId = ref(null);
|
||||
const loadContentCleanRules = async () => {
|
||||
ccrLoading.value = true;
|
||||
try { contentCleanRules.value = await api.get('/api/admin/config/content-clean-rules'); }
|
||||
catch (e) { console.error(e); }
|
||||
finally { ccrLoading.value = false; }
|
||||
};
|
||||
const showAddCleanRule = () => {
|
||||
ccrDialogTitle.value = '新增清理规则';
|
||||
editingCcrId.value = null;
|
||||
ccrForm.id = null; ccrForm.rule_type = 'ai_telltale'; ccrForm.pattern = ''; ccrForm.description = ''; ccrForm.sort_order = 0; ccrForm.is_active = true;
|
||||
ccrDialogVisible.value = true;
|
||||
};
|
||||
const editCleanRule = (row) => {
|
||||
ccrDialogTitle.value = '编辑清理规则';
|
||||
editingCcrId.value = row.id;
|
||||
Object.assign(ccrForm, { id: row.id, rule_type: row.rule_type, pattern: row.pattern, description: row.description || '', sort_order: row.sort_order || 0, is_active: row.is_active });
|
||||
ccrDialogVisible.value = true;
|
||||
};
|
||||
const saveCleanRule = async () => {
|
||||
ccrSaving.value = true;
|
||||
try {
|
||||
const body = { rule_type: ccrForm.rule_type, pattern: ccrForm.pattern, description: ccrForm.description, sort_order: ccrForm.sort_order, is_active: ccrForm.is_active };
|
||||
if (editingCcrId.value) { await api.put('/api/admin/config/content-clean-rules/' + editingCcrId.value, body); ElMessage.success('更新成功'); }
|
||||
else { await api.post('/api/admin/config/content-clean-rules', body); ElMessage.success('创建成功'); }
|
||||
ccrDialogVisible.value = false;
|
||||
await loadContentCleanRules();
|
||||
} catch (e) { ElMessage.error('保存失败: ' + e.message); }
|
||||
finally { ccrSaving.value = false; }
|
||||
};
|
||||
const deleteCleanRule = async (row) => {
|
||||
try { await ElMessageBox.confirm('确定删除该规则吗?', '提示', { type: 'warning' }); await api.delete('/api/admin/config/content-clean-rules/' + row.id); ElMessage.success('删除成功'); await loadContentCleanRules(); }
|
||||
catch (e) { if (e !== 'cancel') ElMessage.error('删除失败: ' + e.message); }
|
||||
};
|
||||
|
||||
const formatDate = (dateStr) => { if (!dateStr) return '-'; return new Date(dateStr.replace(' ', 'T')).toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' }); };
|
||||
const logout = () => { localStorage.removeItem('authToken'); localStorage.removeItem('userRole'); localStorage.removeItem('currentUser'); window.location.href = '/login.html'; };
|
||||
|
||||
@@ -1086,6 +1319,7 @@ const llmConfigs = ref([]);
|
||||
users: fetchUsers,
|
||||
logs: loadLogTypes, orgs: loadOrgs, roles: loadRoles, menus: loadMenus, assistant: loadAssistantConfig,
|
||||
searchproviders: loadSearchProviders,
|
||||
configitems: () => { loadSensitiveWords(); loadContentCleanRules(); },
|
||||
};
|
||||
const loadedTabs = new Set([]);
|
||||
|
||||
@@ -1098,6 +1332,7 @@ const llmConfigs = ref([]);
|
||||
else console.warn('[switchTab] no loader for', name);
|
||||
}
|
||||
};
|
||||
const switchConfigSubTab = (name) => { configSubTab.value = name; };
|
||||
|
||||
onMounted(() => {
|
||||
const hash = window.location.hash.replace('#tab=', '');
|
||||
@@ -1140,6 +1375,9 @@ const llmConfigs = ref([]);
|
||||
searchProviderTestVisible, searchProviderTesting, searchProviderTestResult,
|
||||
loadSearchProviders, addSearchProvider, editSearchProvider, saveSearchProvider, deleteSearchProvider, updateSearchProvider,
|
||||
testSearchProvider, testAllSearchProviders, resetSearchUsage,
|
||||
sensitiveWords, swLoading, swDialogVisible, swSaving, swForm, editingSwId, loadSensitiveWords, showAddSensitiveWord, editSensitiveWord, saveSensitiveWord, deleteSensitiveWord, configSubTab, switchConfigSubTab,
|
||||
contentCleanRules, ccrLoading, ccrDialogVisible, ccrDialogTitle, ccrSaving, ccrForm, editingCcrId,
|
||||
loadContentCleanRules, showAddCleanRule, editCleanRule, saveCleanRule, deleteCleanRule,
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user