'隐私协议'
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
import request from '@/utils/request';
|
||||
import { AxiosPromise } from 'axios';
|
||||
//隐私协议列表
|
||||
export function privacyList(data): AxiosPromise<any> {
|
||||
return request({
|
||||
url: '/admin/v1/agreement/list',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
//协议类型列表
|
||||
export function agreementTypeList(data): AxiosPromise<any> {
|
||||
return request({
|
||||
url: '/admin/v1/agreement/types',
|
||||
method: 'post',
|
||||
data
|
||||
});
|
||||
}
|
||||
//隐私协议添加
|
||||
export function addPrivacy(data): AxiosPromise<any> {
|
||||
return request({
|
||||
url: '/admin/v1/agreement/create',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
//隐私协议修改
|
||||
export function updatePrivacy(data): AxiosPromise<any> {
|
||||
return request({
|
||||
url: '/admin/v1/agreement/update',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//隐私协议删除
|
||||
export function deletePrivacy(data): AxiosPromise<any> {
|
||||
return request({
|
||||
url: '/admin/v1/agreement/delete',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
@@ -10,8 +10,7 @@
|
||||
<!-- 编辑器 -->
|
||||
<Editor
|
||||
:defaultConfig="editorConfig"
|
||||
v-model="modelValue"
|
||||
@onChange="handleChange"
|
||||
v-model="value"
|
||||
style="height: 450px; overflow-y: hidden"
|
||||
:mode="mode"
|
||||
@onCreated="handleCreated"
|
||||
@@ -20,64 +19,66 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onBeforeUnmount, shallowRef, reactive, toRefs } from 'vue';
|
||||
import { onBeforeUnmount, shallowRef, ref, watch } from 'vue';
|
||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
|
||||
import {CODES, Numbers} from "@/utils/code";
|
||||
// API 引用
|
||||
import { Numbers } from "@/utils/code";
|
||||
import useStore from "@/store";
|
||||
const { my } = useStore();
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: [String],
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const value = ref(props.modelValue);
|
||||
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
value.value = newVal;
|
||||
});
|
||||
|
||||
watch(value, (newVal) => {
|
||||
if (newVal !== props.modelValue) {
|
||||
emit('update:modelValue', newVal);
|
||||
}
|
||||
});
|
||||
|
||||
// 编辑器实例,必须用 shallowRef
|
||||
const editorRef = shallowRef();
|
||||
|
||||
const state = reactive({
|
||||
toolbarConfig: {},
|
||||
editorConfig: {
|
||||
const toolbarConfig = {};
|
||||
const editorConfig = {
|
||||
placeholder: '请输入内容...',
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
// 自定义图片上传
|
||||
async customUpload(file: any, insertFn: any) {
|
||||
my.upImage(file, Numbers.imageTypeContent, function (url:string, uri : string){
|
||||
my.upImage(file, Numbers.imageTypeContent, function (url: string) {
|
||||
insertFn(url);
|
||||
})
|
||||
}
|
||||
},
|
||||
uploadVideo: {
|
||||
// 自定义图片上传
|
||||
async customUpload(file: any, insertFn: any) {
|
||||
my.upImage(file, Numbers.imageTypeContent, function (url:string, uri : string){
|
||||
my.upImage(file, Numbers.imageTypeContent, function (url: string) {
|
||||
insertFn(url);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultHtml: props.modelValue,
|
||||
mode: 'default'
|
||||
});
|
||||
|
||||
const { toolbarConfig, editorConfig, defaultHtml, mode } = toRefs(state);
|
||||
};
|
||||
const mode = 'default';
|
||||
|
||||
const handleCreated = (editor: any) => {
|
||||
editorRef.value = editor; // 记录 editor 实例,重要!
|
||||
editorRef.value = editor;
|
||||
if (props.modelValue) {
|
||||
editor.setHtml(props.modelValue);
|
||||
}
|
||||
};
|
||||
|
||||
function handleChange(editor: any) {
|
||||
emit('update:modelValue', editor.getHtml());
|
||||
}
|
||||
|
||||
// 组件销毁时,也及时销毁编辑器
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value;
|
||||
if (editor == null) return;
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<el-dialog :model-value="visible" :title="dialogTitle" width="800px" append-to-body destroy-on-close
|
||||
@update:model-value="$emit('update:visible', $event)">
|
||||
<template v-if="mode === 'view'">
|
||||
<div v-html="formData.content" style="padding: 10px;"></div>
|
||||
</template>
|
||||
<el-form v-else ref="formRef" :model="formData" :rules="rules" label-width="80px">
|
||||
<el-form-item label="类型" prop="type_id">
|
||||
<el-select v-model="formData.type_id" placeholder="请选择类型" style="width: 100%">
|
||||
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="内容" prop="content">
|
||||
<WangEditor v-model="formData.content" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer v-if="mode !== 'view'">
|
||||
<el-button @click="$emit('update:visible', false)">取消</el-button>
|
||||
<el-button type="primary" :loading="loading" @click="handleSubmit">确定</el-button>
|
||||
</template>
|
||||
<template #footer v-else>
|
||||
<el-button @click="$emit('update:visible', false)">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch, computed } from 'vue';
|
||||
import { addPrivacy, updatePrivacy } from '@/api/privacy/index'
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import WangEditor from '@/components/WangEditor/index.vue';
|
||||
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
typeList: any[];
|
||||
mode: 'add' | 'edit' | 'view';
|
||||
editId?: number | undefined;
|
||||
editData?: any;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
const emit = defineEmits<{
|
||||
'update:visible': [val: boolean];
|
||||
'success': [];
|
||||
}>();
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const loading = ref(false);
|
||||
const formData = reactive<{
|
||||
type_id: number | string;
|
||||
content: string;
|
||||
}>({
|
||||
type_id: '',
|
||||
content: '',
|
||||
});
|
||||
|
||||
const rules: FormRules = {
|
||||
type_id: [{ required: true, message: '请选择类型', trigger: 'change' }],
|
||||
content: [{ required: true, message: '请输入内容', trigger: 'blur' }],
|
||||
};
|
||||
|
||||
const dialogTitle = computed(() => {
|
||||
if (props.mode === 'view') return '查看';
|
||||
return props.mode === 'edit' ? '修改' : '添加';
|
||||
});
|
||||
|
||||
const typeName = computed(() => {
|
||||
const item = props.typeList.find((t: any) => t.id === formData.type_id);
|
||||
return item ? item.name : '--';
|
||||
});
|
||||
|
||||
watch(() => props.visible, (val) => {
|
||||
if (val) {
|
||||
if (props.editId) {
|
||||
formData.type_id = props.editData.type_id;
|
||||
formData.content = props.editData.content || '';
|
||||
} else {
|
||||
formData.type_id = '';
|
||||
formData.content = '';
|
||||
}
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
});
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
await formRef.value.validate();
|
||||
loading.value = true;
|
||||
if (props.editId) {
|
||||
await updatePrivacy({ id: props.editId, ...formData });
|
||||
ElMessage.success('修改成功');
|
||||
} else {
|
||||
await addPrivacy(formData);
|
||||
ElMessage.success('添加成功');
|
||||
}
|
||||
emit('update:visible', false);
|
||||
emit('success');
|
||||
} catch (err: any) {
|
||||
if (err !== 'cancel') ElMessage.error(err?.message || '操作失败');
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,157 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-card class="box-card">
|
||||
<el-form ref="queryFormRef" :model="queryParams" inline class="form-row">
|
||||
<el-form-item>
|
||||
<!-- <el-button type="primary" :icon="Search" @click="loadList">搜索</el-button> -->
|
||||
<!-- <el-button :icon="Refresh" @click="resetQuery">重置</el-button> -->
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-left: auto;">
|
||||
<el-button type="primary" :icon="Plus" @click="handleAdd">添加</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="storeList" border stripe style="width: 100%">
|
||||
<el-table-column prop="type_id" label="类型" align="center">
|
||||
<template #default="{ row }"><el-tag type="primary">{{ getTypeName(row.type_id) }}</el-tag></template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="content" label="内容" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="handleView(row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="created_at" label="创建时间" align="center">
|
||||
<template #default="{ row }">{{ row.created_at || '--' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="handleEdit(row)">修改</el-button>
|
||||
<el-button type="primary" link @click="handleDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination :total="total" v-model:page="queryParams.page" v-model:limit="queryParams.size"
|
||||
@pagination="loadList" />
|
||||
</el-card>
|
||||
<!-- 添加/编辑弹窗 -->
|
||||
<PrivacyDialog v-model:visible="dialogVisible" :type-list="typeList" :mode="dialogMode" :edit-id="editId" :edit-data="editRow"
|
||||
@success="loadList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, onMounted } from 'vue';
|
||||
import { privacyList, deletePrivacy, updatePrivacy, addPrivacy, agreementTypeList } from '@/api/privacy/index'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Search, Refresh, Plus } from '@element-plus/icons-vue';
|
||||
import PrivacyDialog from './components/PrivacyDialog.vue';
|
||||
|
||||
// 类型列表
|
||||
const typeList = ref<any[]>([]);
|
||||
|
||||
// 列表
|
||||
const loading = ref(false);
|
||||
const total = ref(0);
|
||||
const storeList = ref<any[]>([]);
|
||||
const queryFormRef = ref();
|
||||
const queryParams = reactive({
|
||||
page: 1,
|
||||
size: 10,
|
||||
keyword: '',
|
||||
});
|
||||
|
||||
// 弹窗
|
||||
const dialogVisible = ref(false);
|
||||
const dialogMode = ref<'add' | 'edit' | 'view'>('add');
|
||||
const editId = ref<number | undefined>(undefined);
|
||||
const editRow = ref<any>(undefined);
|
||||
|
||||
const handleAdd = () => {
|
||||
dialogMode.value = 'add';
|
||||
editId.value = undefined;
|
||||
editRow.value = undefined;
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
const handleEdit = (row: any) => {
|
||||
dialogMode.value = 'edit';
|
||||
editId.value = row.id;
|
||||
editRow.value = { ...row };
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
const handleView = (row: any) => {
|
||||
dialogMode.value = 'view';
|
||||
editId.value = row.id;
|
||||
editRow.value = { ...row };
|
||||
dialogVisible.value = true;
|
||||
};
|
||||
|
||||
function normalizePageData(data: any): { list: any[]; total: number } {
|
||||
if (Array.isArray(data)) return { list: data, total: data.length };
|
||||
const list = data?.list ?? data?.data ?? [];
|
||||
const total = Number(data?.total);
|
||||
return { list, total: Number.isFinite(total) ? total : list.length };
|
||||
}
|
||||
|
||||
// 加载类型列表
|
||||
const loadTypeList = () => {
|
||||
agreementTypeList({}).then(({ data }) => {
|
||||
typeList.value = Array.isArray(data) ? data : (data?.list ?? []);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取类型名称
|
||||
const getTypeName = (typeId: number) => {
|
||||
const item = typeList.value.find((t: any) => t.id === typeId);
|
||||
return item ? item.name : '--';
|
||||
};
|
||||
|
||||
// 加载列表
|
||||
const loadList = () => {
|
||||
loading.value = true;
|
||||
privacyList(queryParams)
|
||||
.then(({ data }) => {
|
||||
const { list, total: t } = normalizePageData(data);
|
||||
storeList.value = list;
|
||||
total.value = t;
|
||||
})
|
||||
.catch(() => ElMessage.error('获取列表失败'))
|
||||
.finally(() => (loading.value = false));
|
||||
};
|
||||
|
||||
// 重置
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields();
|
||||
queryParams.page = 1;
|
||||
loadList();
|
||||
};
|
||||
|
||||
// 删除
|
||||
const handleDelete = async (row: any) => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确认删除?', '提示', { type: 'warning' });
|
||||
await deletePrivacy({ id: row.id });
|
||||
ElMessage.success('删除成功');
|
||||
loadList();
|
||||
} catch (err: any) {
|
||||
if (err !== 'cancel') ElMessage.error(err?.message || '删除失败');
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadTypeList();
|
||||
loadList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
}
|
||||
.form-row {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user