manager 升级到vue3

This commit is contained in:
pikachu1995@126.com
2026-05-25 10:49:09 +08:00
parent e7350899bf
commit 615ee91511
239 changed files with 22907 additions and 30841 deletions

View File

@@ -1,164 +1,108 @@
<template>
<div>
<Card>
<el-card>
<div class="operation mb_10">
<Button @click="addParent" type="primary" icon="md-add"
>添加一级分类</Button
>
<el-button type="primary" @click="addParent">添加一级分类</el-button>
</div>
<tree-table
ref="treeTable"
size="default"
:loading="loading"
<el-table
v-loading="loading"
:data="tableData"
:columns="columns"
:border="true"
:show-index="false"
:is-fold="true"
:expand-type="false"
primary-key="id"
row-key="id"
border
default-expand-all
:tree-props="{ children: 'children' }"
style="width: 100%"
>
<template slot="action" slot-scope="scope">
<a
style="color:#2d8cf0;cursor:pointer;text-decoration:none;margin-right:5px"
@click="edit(scope.row)"
>编辑</a>
<span style="margin:0 8px;color:#dcdee2">|</span>
<a
style="color:#2d8cf0;cursor:pointer;text-decoration:none;margin-right:5px"
@click="remove(scope.row)"
>删除</a>
<span v-show="scope.row.level != 1" style="margin:0 8px;color:#dcdee2">|</span>
<a
v-show="scope.row.level != 1"
style="color:#2d8cf0;cursor:pointer;text-decoration:none"
@click="addChildren(scope.row)"
>添加子分类</a>
</template>
</tree-table>
</Card>
<Modal
:title="modalTitle"
<el-table-column prop="articleCategoryName" label="分类名称" min-width="200" />
<el-table-column prop="sort" label="排序" width="120" />
<el-table-column label="操作" width="320" align="center">
<template #default="{ row }">
<template v-if="row">
<a class="link-text" @click="edit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
<template v-if="row.level != 1">
<span class="op-split">|</span>
<a class="link-text" @click="addChildren(row)">添加子分类</a>
</template>
</template>
</template>
</el-table-column>
</el-table>
</el-card>
<el-dialog
v-model="modalVisible"
:mask-closable="false"
:width="500"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<Form
ref="form"
:model="formAdd"
:label-width="100"
:rules="formValidate"
>
<div v-if="showParent">
<FormItem label="上级分类" prop="parentId">
{{ parentTitle }}
<Input
v-model="formAdd.parentId"
clearable
style="width: 100%; display: none"
/>
</FormItem>
</div>
<FormItem label="分类名称" prop="articleCategoryName">
<Input
v-model="formAdd.articleCategoryName"
clearable
style="width: 100%"
/>
</FormItem>
<FormItem label="排序值" prop="sort">
<InputNumber v-model="formAdd.sort"></InputNumber>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="Submit"
>提交</Button
>
</div>
</Modal>
<el-form ref="form" :model="formAdd" label-width="100px" :rules="formValidate">
<el-form-item v-if="showParent" label="上级分类" prop="parentId">
{{ parentTitle }}
<el-input v-model="formAdd.parentId" style="display: none" />
</el-form-item>
<el-form-item label="分类名称" prop="articleCategoryName">
<el-input v-model="formAdd.articleCategoryName" clearable />
</el-form-item>
<el-form-item label="排序值" prop="sort">
<el-input-number v-model="formAdd.sort" style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="Submit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import {
saveArticleCategory,
getArticleCategory,
delArticleCategory,
updateArticleCategory,
} from "@/api/pages";
import TreeTable from "@/components/tree-table/Table/Table";
import uploadPicInput from "@/components/lili/upload-pic-input";
import { saveArticleCategory, getArticleCategory, delArticleCategory, updateArticleCategory } from "@/api/pages";
import { regular } from "@/utils";
export default {
name: "lili-components",
components: {
TreeTable,
uploadPicInput,
},
data() {
return {
submitLoading:false,
loading: false, // 加载状态
expandLevel: 1, // 展开的层级
modalType: 0, // 添加或编辑标识
modalVisible: false, // 添加或编辑显示
modalTitle: "", // 添加或编辑标题
showParent: false, // 是否展示上级菜单
parentTitle: "", // 父级菜单名称
submitLoading: false,
loading: false,
expandLevel: 1,
modalType: 0,
modalVisible: false,
modalTitle: "",
showParent: false,
parentTitle: "",
formAdd: {
// 添加或编辑表单对象初始化数据
parentId: "",
sort: 1,
level: 0,
articleCategoryName: "",
},
// 表单验证规则
formValidate: {
articleCategoryName: [regular.REQUIRED],
sort: [regular.REQUIRED,regular.INTEGER],
sort: [regular.REQUIRED, regular.INTEGER],
},
columns: [
{
title: "分类名称",
key: "articleCategoryName",
witt: "100px",
},
{
title: "排序",
key: "sort",
width: "100px",
},
{
title: "操作",
key: "action",
align: "center",
headerAlign: "center",
width: "400px",
type: "template",
template: "action",
},
],
tableData: [], // 表格数据
tableData: [],
};
},
methods: {
// 初始化数据
init() {
this.getAllList();
},
// 添加子分类
addChildren(v) {
this.modalType = 0;
this.modalTitle = "添加子分类";
this.formAdd.articleCategoryName = "";
this.parentTitle = v.articleCategoryName;
this.formAdd.level = eval(v.level + "+1");
this.formAdd.level = Number(v.level) + 1;
this.showParent = true;
delete this.formAdd.id;
this.formAdd.parentId = v.id;
this.modalVisible = true;
},
// 编辑分类
edit(v) {
this.modalType = 1;
this.modalTitle = "编辑";
@@ -170,68 +114,55 @@ export default {
this.showParent = false;
this.modalVisible = true;
},
// 添加一级分类
addParent() {
this.modalType = 0;
this.modalTitle = "添加一级分类";
this.parentTitle = "顶级分类";
this.showParent = true;
this.$refs.form.resetFields();
this.$refs.form?.resetFields();
delete this.formAdd.id;
this.formAdd.parentId = 0;
this.modalVisible = true;
},
// 提交
Submit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalType === 0) {
// 添加 避免编辑后传入id等数据 记得删除
delete this.formAdd.id;
saveArticleCategory(this.formAdd).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("添加成功");
this.formAdd = {
// 添加或编辑表单对象初始化数据
parentId: "",
sort: 1,
level: 0,
articleCategoryName: "",
};
} else {
// this.$Message.error(res.message);
}
this.getAllList();
this.modalVisible = false;
});
} else {
// 编辑
updateArticleCategory(this.formAdd, this.formAdd.id).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("修改成功");
} else {
// this.$Message.error(res.message);
}
this.getAllList();
this.modalVisible = false;
this.$refs.form.resetFields();
});
}
if (!valid) return;
this.submitLoading = true;
if (this.modalType === 0) {
delete this.formAdd.id;
saveArticleCategory(this.formAdd).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("添加成功");
this.formAdd = {
parentId: "",
sort: 1,
level: 0,
articleCategoryName: "",
};
}
this.getAllList();
this.modalVisible = false;
});
} else {
updateArticleCategory(this.formAdd, this.formAdd.id).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("修改成功");
}
this.getAllList();
this.modalVisible = false;
this.$refs.form.resetFields();
});
}
});
},
// 删除分类
remove(v) {
this.$Modal.confirm({
title: "确认删除",
content: "您确认要删除 " + v.articleCategoryName + " ?",
loading: true,
onOk: () => {
// 删除
delArticleCategory(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {
@@ -242,74 +173,17 @@ export default {
},
});
},
// 获取分类数据
getAllList(newval) {
getAllList() {
this.loading = true;
getArticleCategory().then((res) => {
this.loading = false;
if (res.success) {
// 仅展开指定级数 默认后台已展开所有
let expandLevel = this.expandLevel;
res.result.forEach(function (e) {
if (expandLevel == 1) {
if (e.level == 0) {
e.expand = false;
}
if (e.children && e.children.length > 0) {
e.children.forEach(function (c) {
if (c.level == 1) {
c.expand = false;
}
if (c.children && c.children.length > 0) {
c.children.forEach(function (b) {
if (b.level == 2) {
b.expand = false;
}
});
}
});
}
} else if (expandLevel == 2) {
if (e.level == 0) {
e.expand = true;
}
if (e.children && e.children.length > 0) {
e.children.forEach(function (c) {
if (c.level == 1) {
c.expand = false;
}
if (c.children && c.children.length > 0) {
c.children.forEach(function (b) {
if (b.level == 2) {
b.expand = false;
}
});
}
});
}
} else if (expandLevel == 3) {
if (e.level == 0) {
e.expand = true;
}
if (e.children && e.children.length > 0) {
e.children.forEach(function (c) {
if (c.level == 1) {
c.expand = true;
}
if (c.children && c.children.length > 0) {
c.children.forEach(function (b) {
if (b.level == 2) {
b.expand = false;
}
});
}
});
}
}
});
this.tableData = res.result;
}
});
getArticleCategory()
.then((res) => {
if (res.success) {
this.tableData = res.result || [];
}
})
.finally(() => {
this.loading = false;
});
},
},
mounted() {
@@ -317,10 +191,18 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.article {
font-size: 16px;
font-weight: 400;
margin: 12px 0;
.link-text {
color: #409eff;
cursor: pointer;
text-decoration: none;
}
.op-split {
margin: 0 8px;
color: #dcdee2;
}
.mb_10 {
margin-bottom: 10px;
}
</style>

View File

@@ -1,134 +1,99 @@
<template>
<div class="search">
<Card>
<Row class="operation">
<Button @click="add()" type="primary">设置今日热词</Button>
</Row>
<Row>
<p>
<Alert type="success">
这里展示今日系统中搜索前一百的搜索热词,分数为热词在排序系统中的分数,分数越高,可以在用户获取热词时进行优先展示(首页商品搜索栏下方推荐位)(分数可以填写负数,会降低推荐度)
</Alert>
</p>
</Row>
<div class="card-list">
<Card v-for="words in data" class="card-item" :key="words" onclick="">
<p>
<a href="#" slot="extra" @click.prevent="add(words)">{{ words }}</a>
</p>
<p slot="extra">
<a @click="deleteWords(words)">
<Icon type="md-close"/>
</a>
</p>
</Card>
</div>
</Card>
<Modal :title="modalTitle" v-model="modalVisible" :mask-closable="false" :width="500">
<Form ref="form" :model="form" :label-width="100" :rules="formValidate">
<FormItem label="热词" prop="keywords">
<Input v-model="form.keywords" clearable style="width: 100%"/>
</FormItem>
<FormItem label="分数" prop="point">
<Input v-model="form.point" clearable style="width: 100%"/>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交
</Button
>
</div>
</Modal>
<div class="search box">
<el-card>
<el-date-picker
v-model="params.date"
type="date"
placeholder="选择查看日期"
value-format="YYYY-MM-DD"
:disabled-date="disabledDate"
style="width: 200px"
@change="search"
/>
<el-alert
class="mt_10"
type="success"
:closable="false"
show-icon
title="这里展示历史某一天的热词数据统计,可根据需求配置每日持久化多少条数据。"
/>
<div id="container"></div>
</el-card>
</div>
</template>
<script>
import {getHotWords, setHotWords, deleteHotWords} from "@/api/index";
import {regular} from "@/utils";
import { Chart } from "@antv/g2";
import { getHotWordsHistory } from "@/api/index";
import { Message } from "@/utils/message";
export default {
name: "hotWords",
components: {},
name: "HOTWORDS_HISTORY",
data() {
return {
submitLoading: false,
modalTitle: "",
loading: true, // 表单加载状态
modalVisible: false, //弹出框
form: {
keywords: "",
point: 0,
},
data: [], // 表单数据
// 表单验证规则
formValidate: {
keywords: [regular.REQUIRED, regular.VARCHAR20],
point: [regular.REQUIRED, regular.NUMBER],
params: {
date: "",
},
hotWordsChart: null,
hotWordsData: [],
endMonth: null,
};
},
created() {
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
this.params.date = this.$filters.unixToDate(Math.floor(yesterday.getTime() / 1000), "yyyy-MM-dd");
},
methods: {
init() {
this.getDataList();
},
getDataList() {
this.loading = true;
getHotWords().then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result;
}
});
this.loading = false;
},
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
setHotWords(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
this.$Message.success("操作成功");
this.getDataList();
this.modalVisible = false;
}
});
}
});
},
//设置热词优先
add(words) {
this.modalType = 0;
this.modalTitle = "设置热词";
if (words) {
this.form.keywords = words;
} else {
this.form.keywords = "";
disabledDate(date) {
if (this.endMonth) {
const endDate = this.getDate(this.endMonth);
return (date && date > endDate) || date > new Date();
}
this.form.point = 1;
this.modalVisible = true;
return date && date > new Date();
},
deleteWords(words) {
this.$Modal.confirm({
title: "是否确定删除热词",
content: "<p>您确定要删除此热词吗?</p>",
okText: "确实",
cancelText: "取消",
onOk: () => {
let params = {words: words}
deleteHotWords(params).then((res) => {
if (res.success) {
this.$Message.success("删除成功");
this.getDataList();
}
});
getDate(monthStr) {
const [year, month] = monthStr.split("-");
return new Date(year, month, 0);
},
async search(val) {
if (val) {
this.params.date = val;
}
const res = await getHotWordsHistory(this.params);
if (res.success) {
this.hotWordsData = res.result || [];
if (this.hotWordsChart) {
this.hotWordsChart.changeData(this.hotWordsData);
this.hotWordsChart.render();
}
if (!this.hotWordsData.length) {
Message.error("暂无数据");
}
}
},
init() {
const chart = new Chart({
container: "container",
autoFit: true,
height: 500,
padding: [50, 20, 50, 20],
});
chart.scale("score", {
alias: "搜索次数",
});
chart.axis("keywords", {
tickLine: {
alignTick: false,
},
});
chart.axis("score", false);
chart.tooltip({
showMarkers: false,
});
chart.interval().position("keywords*score").color("#f59b99");
chart.interaction("element-active");
this.hotWordsChart = chart;
this.search();
},
},
mounted() {
@@ -136,22 +101,9 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.card-list {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.card-item {
min-width: 100px;
margin: 20px;
}
</style>
<style>
.ivu-card-extra {
right: 3px;
top: 15px;
z-index: 99;
<style lang="scss" scoped>
.box {
min-height: 400px;
}
</style>

View File

@@ -1,71 +1,69 @@
<template>
<div class="search">
<Card>
<Row class="operation">
<Button @click="add()" type="primary">设置今日热词</Button>
</Row>
<Row>
<p>
<Alert type="success">
这里展示今日系统中搜索前一百的搜索热词,分数为热词在排序系统中的分数,分数越高,可以在用户获取热词时进行优先展示(首页商品搜索栏下方推荐位)(分数可以填写负数,会降低推荐度)
</Alert>
</p>
</Row>
<el-card>
<div class="operation mb_10">
<el-button type="primary" @click="add()">设置今日热词</el-button>
</div>
<el-alert
type="success"
:closable="false"
show-icon
title="这里展示今日系统中搜索前一百的搜索热词,分数为热词在排序系统中的分数,分数越高,可以在用户获取热词时进行优先展示(首页商品搜索栏下方推荐位)(分数可以填写负数,会降低推荐度)"
/>
<div class="card-list">
<Card v-for="words in data" class="card-item" :key="words" onclick="">
<p>
<a href="#" slot="extra" @click.prevent="add(words)">{{ words }}</a>
</p>
<p slot="extra">
<a @click="deleteWords(words)">
<Icon type="md-close"/>
</a>
</p>
</Card>
<el-card v-for="words in data" :key="words" class="card-item" shadow="hover">
<div class="card-item-inner">
<a href="#" class="word-link" @click.prevent="add(words)">{{ words }}</a>
<el-icon class="close-icon" @click="deleteWords(words)"><Close /></el-icon>
</div>
</el-card>
</div>
</Card>
<Modal :title="modalTitle" v-model="modalVisible" :mask-closable="false" :width="500">
<Form ref="form" :model="form" :label-width="100" :rules="formValidate">
<FormItem label="热词" prop="keywords">
<Input v-model="form.keywords" clearable style="width: 100%"/>
</FormItem>
<FormItem label="分数" prop="point">
<Input v-model="form.point" clearable style="width: 100%"/>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交
</Button
>
</div>
</Modal>
</el-card>
<el-dialog
v-model="modalVisible"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
append-to-body
destroy-on-close
>
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="热词" prop="keywords">
<el-input v-model="form.keywords" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="分数" prop="point">
<el-input v-model="form.point" clearable style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import {getHotWords, setHotWords, deleteHotWords} from "@/api/index";
import {regular} from "@/utils";
import { Close } from "@element-plus/icons-vue";
import { getHotWords, setHotWords, deleteHotWords } from "@/api/index";
import { regular } from "@/utils";
export default {
name: "hotWords",
components: {},
name: "HOTWORDS_TODAY",
components: { Close },
data() {
return {
submitLoading: false,
modalTitle: "",
loading: true, // 表单加载状态
modalVisible: false, //弹出框
loading: true,
modalVisible: false,
form: {
keywords: "",
point: 0,
},
data: [], // 表单数据
// 表单验证规则
data: [],
formValidate: {
keywords: [regular.REQUIRED, regular.VARCHAR20],
point: [regular.REQUIRED, regular.NUMBER],
@@ -84,7 +82,6 @@ export default {
this.data = res.result;
}
});
this.loading = false;
},
handleSubmit() {
this.$refs.form.validate((valid) => {
@@ -101,27 +98,20 @@ export default {
}
});
},
//设置热词优先
add(words) {
this.modalType = 0;
this.modalTitle = "设置热词";
if (words) {
this.form.keywords = words;
} else {
this.form.keywords = "";
}
this.form.keywords = words || "";
this.form.point = 1;
this.modalVisible = true;
},
deleteWords(words) {
this.$Modal.confirm({
title: "是否确定删除热词",
content: "<p>您确定要删除此热词吗?</p>",
okText: "确实",
content: "您确定要删除此热词吗?",
okText: "确定",
cancelText: "取消",
onOk: () => {
let params = {words: words}
deleteHotWords(params).then((res) => {
deleteHotWords({ words }).then((res) => {
if (res.success) {
this.$Message.success("删除成功");
this.getDataList();
@@ -136,22 +126,38 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.card-list {
display: flex;
flex-wrap: wrap;
align-items: center;
margin-top: 16px;
}
.card-item {
min-width: 100px;
margin: 20px;
}
</style>
<style>
.ivu-card-extra {
right: 3px;
top: 15px;
z-index: 99;
margin: 10px;
}
.card-item-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.word-link {
color: var(--el-color-primary);
text-decoration: none;
}
.close-icon {
cursor: pointer;
color: var(--el-text-color-secondary);
&:hover {
color: var(--el-color-danger);
}
}
</style>

View File

@@ -1,53 +1,91 @@
<template>
<div class="layout">
<Form ref="formValidate" :label-width="150" label-position="right" :model="formValidate" :rules="ruleValidate">
<FormItem class="label-item" v-for="(point,index) in formValidate.pointSettingItems" :key="index"
:label="'签到设置'+(index+1)">
<div class="label-item">
<InputNumber :min="1" v-model="point.day"></InputNumber>
<InputNumber :min="0" v-model="point.point"></InputNumber>
<Button ghost type="error" @click="delSign(point,index)">删除</Button>
<span class="ml_10">签到<span class="theme_color">{{ point.day }}</span>天,赠送<span
class="theme_color">{{ point.point }}</span>积分</span>
<el-form
ref="formValidate"
label-width="150px"
label-position="right"
:model="formValidate"
:rules="ruleValidate"
>
<el-form-item label="热词默认配置" prop="hotWordsSettingItems">
<div
class="item-label"
v-for="(item, index) in formValidate.hotWordsSettingItems"
:key="index"
>
<div>
<div class="item-keyword">
<div>热词:</div>
<el-input v-model="item.keywords" />
</div>
<div class="item-score">
<div>分数:</div>
<el-input-number :max="5" :min="0" v-model="item.score" />
</div>
</div>
<div>
<el-button type="primary" @click="formValidate.hotWordsSettingItems.splice(index, 1)">
删除
</el-button>
</div>
</div>
<el-button @click="addSetItem">添加配置</el-button>
</el-form-item>
<el-form-item label="每日持久化热词数量" prop="saveNum">
<el-input-number :min="0" v-model="formValidate.saveNum" />
</el-form-item>
</FormItem>
<FormItem label="操作:">
<Button @click="addSign">新增签到</Button>
</FormItem>
<div class="label-btns">
<Button type="primary" @click="submit('formValidate')">保存</Button>
<el-button type="primary" @click="submit('formValidate')">保存</el-button>
</div>
</Form>
</el-form>
</div>
</template>
<script>
import {setSetting} from "@/api/index";
import { setSetting } from "@/api/index";
export default {
props: ["res", "type"],
data() {
return {
formValidate: {}, // 表单数据
ruleValidate: {},
formValidate: {
saveNum: 1,
hotWordsSettingItems: [
{
keywords: "",
score: 1,
},
],
},
};
},
props: ["res", "type"],
created() {
this.init();
},
methods: {
// 保存
submit(name) {
let that = this;
this.setupSetting();
addSetItem() {
if (this.formValidate.hotWordsSettingItems.length >= 5) {
this.$Message.error("最多5个热词项");
} else {
this.formValidate.hotWordsSettingItems.push({
keywords: "",
score: 1,
});
}
},
submit(name) {
this.$refs[name].validate((valid) => {
if (valid) {
this.setupSetting();
} else {
this.$Message.error("请正确填写内容!");
}
});
},
// 保存设置
setupSetting() {
setSetting(this.type, this.formValidate).then((res) => {
setSetting(this.type || "HOT_WORDS", this.formValidate).then((res) => {
if (res.success) {
this.$Message.success("保存成功!");
} else {
@@ -55,52 +93,14 @@ export default {
}
});
},
delSign(item, index) {
this.formValidate.pointSettingItems.splice(index, 1);
},
addSign() {
if (this.formValidate.pointSettingItems.length >= 5) {
this.$Message.error({
content: "最多设置5项签到设置",
});
return false;
}
this.formValidate.pointSettingItems.push({
point: "0",
day: this.formValidate.pointSettingItems.length,
});
},
// 实例化数据
init() {
this.res = JSON.parse(this.res);
Object.keys(this.res).map((item) => {
if (item == "pointSettingItems") {
return false;
}
this.res[item] += "";
});
this.$set(this, "formValidate", {...this.res});
Object.keys(this.formValidate).forEach((item) => {
this.ruleValidate[item] = [
{
required: true,
message: "请填写必填项",
trigger: "blur",
},
{
validator: (rule, value, callback) => {
if (value < 0) {
callback(new Error("不能输入负数!"));
} else {
callback();
}
},
trigger: "change",
},
];
});
if (!this.res) {
return;
}
const parsed = typeof this.res === "string" ? JSON.parse(this.res) : this.res;
if (parsed.hotWordsSettingItems) {
this.formValidate = { ...parsed };
}
},
},
};
@@ -109,37 +109,27 @@ export default {
<style lang="scss" scoped>
@import "./style.scss";
.label-item {
.item-label {
border-bottom: 1px solid #ededed;
margin-bottom: 10px;
display: flex;
align-items: center;
width: 500px;
justify-content: space-between;
}
> .ivu-input-number {
width: 100px;
margin-right: 5px;
}
.item-keyword,
.item-score {
display: flex;
align-items: center;
margin-bottom: 10px;
> .ivu-input-number:nth-last-of-type(1) {
width: 150px;
margin-right: 5px;
}
> .ivu-input {
width: 100px;
margin: 0 10px;
> div {
margin-right: 20px;
}
}
::v-deep .ivu-input {
width: 70px !important;
}
.ivu-input-wrapper {
width: 70px;
margin-right: 10px;
}
.label-btns {
::v-deep .ivu-btn {
margin-right: 10px;
}
.item-keyword .el-input {
width: 200px;
}
</style>

View File

@@ -1,138 +1,146 @@
<template>
<div class="wrapper">
<Row>
<Col style="height: 100%" span="4">
<Card class="article-category mr_10">
<Tree :data="treeData" @on-select-change="handleCateChange"></Tree>
</Card>
</Col>
<Col span="20">
<Card class="article-detail">
<Row @keydown.enter.native="handleSearch">
<Form
ref="searchForm"
:model="searchForm"
inline
:label-width="70"
style="width: 100%"
class="search-form"
>
<Form-item label="文章标题" prop="title">
<Input
type="text"
v-model="searchForm.title"
placeholder="请输入文章标题"
clearable
style="width: 200px"
<el-row :gutter="10">
<el-col :span="4" style="height: 100%">
<el-card class="article-category mr_10">
<el-tree
:data="treeData"
:props="{ label: 'title', children: 'children' }"
node-key="value"
highlight-current
default-expand-all
@node-click="handleCateChange"
/>
</el-card>
</el-col>
<el-col :span="20">
<el-card class="article-detail">
<el-form
ref="searchForm"
:model="searchForm"
inline
label-width="70px"
style="width: 100%"
class="search-form"
@keyup.enter="handleSearch"
>
<el-form-item label="文章标题" prop="title">
<el-input
v-model="searchForm.title"
placeholder="请输入文章标题"
clearable
style="width: 200px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" class="search-btn" @click="handleSearch">搜索</el-button>
</el-form-item>
</el-form>
<div class="operation padding-row">
<el-button v-if="!selected" type="primary" @click="add">添加</el-button>
</div>
<el-table ref="table" v-loading="loading" border :data="data" style="width: 100%">
<el-table-column prop="articleCategoryName" label="分类名称" width="150" />
<el-table-column prop="title" label="文章标题" min-width="200" show-overflow-tooltip />
<el-table-column label="是否显示" width="100">
<template #default="{ row }">
<el-switch
v-if="row"
v-model="row.openStatus"
inline-prompt
active-text="展示"
inactive-text="隐藏"
@change="changeSwitch(row)"
/>
</Form-item>
<Button
@click="handleSearch"
type="primary"
icon="ios-search"
class="search-btn"
>搜索</Button
>
</Form>
</Row>
<Row class="operation padding-row">
<Button @click="add" v-if="!selected" type="primary">添加</Button>
</Row>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
ref="table"
>
<!-- 页面展示 -->
<template slot="openStatusSlot" slot-scope="scope">
<div></div>
<i-switch
size="large"
v-model="scope.row.openStatus"
@on-change="changeSwitch(scope.row)"
>
<span slot="open">展示</span>
<span slot="close">隐藏</span>
</i-switch>
</template>
</Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
</template>
</el-table-column>
<el-table-column prop="sort" label="排序" width="100" />
<el-table-column label="操作" width="230" align="center">
<template #default="{ row, $index }">
<template v-if="row">
<a
v-if="selected"
class="link-text"
@click="selectRow(row, $index)"
>
{{ selectedIndex == $index ? "已选" : "选择" }}
</a>
<span v-if="selected" class="op-split">|</span>
<a class="link-text" @click="edit(row)">编辑</a>
<span class="op-split">|</span>
<a class="link-text" @click="remove(row)">删除</a>
</template>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
size="small"
show-total
show-elevator
>
</Page>
</Row>
</Card>
</Col>
</Row>
<template v-if="!selected">
<Modal
:title="modalTitle"
v-model="modalVisible"
:mask-closable="false"
:width="1100"
>
<Form ref="form" :model="form" :label-width="100">
<FormItem label="文章标题" prop="title">
<Input v-model="form.title" clearable style="width: 40%" />
</FormItem>
<FormItem label="文章分类" prop="categoryId">
<Select
v-model="treeValue"
placeholder="请选择"
clearable
style="width: 180px"
>
<Option v-if="treeValue" :value="treeValue" style="display: none"
>{{ treeValue }}
</Option>
<Tree
:data="treeDataDefault"
@on-select-change="handleCheckChange"
></Tree>
</Select>
</FormItem>
<FormItem label="文章排序" prop="sort">
<Input
type="number"
v-model="form.sort"
clearable
style="width: 10%"
@current-change="changePage"
@size-change="changePageSize"
/>
</FormItem>
<FormItem class="form-item-view-el" label="文章内容" prop="content">
<tinymec
ref="editor"
openXss
v-model="form.content"
v-if="modalVisible"
></tinymec>
</FormItem>
<FormItem label="是否展示" prop="openStatus">
<i-switch size="large" v-model="form.openStatus">
<span slot="open">展示</span>
<span slot="close">隐藏</span>
</i-switch>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交</Button
>
</div>
</Modal>
</template>
</div>
</el-card>
</el-col>
</el-row>
<el-dialog
v-if="!selected"
v-model="modalVisible"
:title="modalTitle"
width="1100px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="form" label-width="100px">
<el-form-item label="文章标题" prop="title">
<el-input v-model="form.title" clearable style="width: 40%" />
</el-form-item>
<el-form-item label="文章分类" prop="categoryId">
<el-tree-select
v-model="form.categoryId"
:data="treeDataDefault"
:props="{ label: 'title', value: 'value', children: 'children' }"
placeholder="请选择"
clearable
check-strictly
style="width: 180px"
@change="handleCategoryChange"
/>
</el-form-item>
<el-form-item label="文章排序" prop="sort">
<el-input v-model="form.sort" type="number" clearable style="width: 10%" />
</el-form-item>
<el-form-item class="form-item-view-el" label="文章内容" prop="content">
<tinymec
v-if="modalVisible"
ref="editor"
open-xss
v-model="form.content"
/>
</el-form-item>
<el-form-item label="是否展示" prop="openStatus">
<el-switch
v-model="form.openStatus"
inline-prompt
active-text="展示"
inactive-text="隐藏"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
@@ -147,9 +155,10 @@ import {
updateArticleStatus,
} from "@/api/pages";
import tinymec from "@/components/editor/index.vue";
export default {
components: {
tinymec: tinymec,
tinymec,
},
props: {
selected: {
@@ -159,23 +168,21 @@ export default {
},
data() {
return {
selectedIndex: 99999, // 已选下标
loading: true, // 表单加载状态
modalType: 0, // 添加或编辑标识
modalVisible: false, // 添加或编辑显示
modalTitle: "", // 添加或编辑标题
selectedIndex: 99999,
loading: true,
modalType: 0,
modalVisible: false,
modalTitle: "",
treeDataDefault: [],
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "createTime", // 默认排序字段
order: "desc", // 默认排序方式
categoryId: "",
},
searchTreeValue: "", // 切换
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
categoryId: "",
},
searchTreeValue: "",
form: {
// 添加或编辑表单对象初始化数据
openStatus: false,
title: "",
categoryId: "",
@@ -183,111 +190,12 @@ export default {
content: "",
id: "",
},
list: [], // 列表
treeValue: "", // 选择的分类
//树结构
list: [],
treeValue: "",
treeData: [],
submitLoading: false, // 添加或编辑提交状态
columns: [
// 表头
{
title: "分类名称",
key: "articleCategoryName",
width: 150,
},
{
title: "文章标题",
key: "title",
minWidth: 200,
tooltip: true,
},
{
title: "是否显示",
key: "openStatus",
width: 100,
slot: "openStatusSlot",
},
{
title: "排序",
key: "sort",
width: 100,
},
{
title: "操作",
key: "action",
align: "center",
width: 230,
render: (h, params) => {
return h("div", [
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
marginRight: "5px",
display: this.selected ? "" : "none",
},
on: {
click: () => {
this.selectedIndex = params.index;
this.$emit("callbacked", params.row);
},
},
},
this.selectedIndex == params.index ? "已选" : "选择"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
marginRight: "5px",
},
on: {
click: () => {
this.edit(params.row);
},
},
},
"编辑"
),
h(
"span",
{ style: { margin: "0 8px", color: "#dcdee2" } },
"|"
),
h(
"a",
{
style: {
color: "#2d8cf0",
cursor: "pointer",
textDecoration: "none",
},
on: {
click: () => {
this.remove(params.row);
},
},
},
"删除"
),
]);
},
},
],
data: [], // 表单数据
total: 0, // 表单数据总数
submitLoading: false,
data: [],
total: 0,
};
},
watch: {
@@ -305,68 +213,69 @@ export default {
},
},
methods: {
// 初始化数据
init() {
this.getDataList();
this.getAllList(0);
},
// 选择分类回调
selectRow(row, index) {
this.selectedIndex = index;
this.$emit("callbacked", row);
},
handleCateChange(data) {
let { value, title } = data[0];
this.list.push({
value,
title,
});
if (!data) return;
const { value, title } = data;
this.list.push({ value, title });
this.searchForm.categoryId = value;
this.searchTreeValue = title;
},
//是否展示文章
changeSwitch(v) {
let params = {
status: v.openStatus,
};
const params = { status: v.openStatus };
updateArticleStatus(v.id, params).then((res) => {
this.submitLoading = false;
if (res.success) {
}
});
},
// 文章分类的选择事件
handleCategoryChange(value) {
this.form.categoryId = value;
const findTitle = (nodes, id) => {
for (const node of nodes || []) {
if (node.value === id) return node.title;
const childTitle = findTitle(node.children, id);
if (childTitle) return childTitle;
}
return "";
};
this.treeValue = findTitle(this.treeDataDefault, value);
},
handleCheckChange(data) {
let value = "";
let title = "";
this.list = [];
data.forEach((item, index) => {
data.forEach((item) => {
value += `${item.value},`;
title += `${item.title},`;
});
value = value.substring(0, value.length - 1);
title = title.substring(0, title.length - 1);
this.list.push({
value,
title,
});
this.list.push({ value, title });
this.form.categoryId = value;
this.treeValue = title;
},
// 改变页数
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
},
// 改变页码
changePageSize(v) {
this.selected.pageNumber = 1;
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList();
},
// 搜索列表
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
// 获取全部文章分类
getAllList(parent_id) {
this.loading = true;
getArticleCategory(parent_id).then((res) => {
@@ -380,28 +289,25 @@ export default {
children: [],
id: "0",
categoryId: 0,
value: "0",
});
}
});
},
// 文章分类格式化方法
getTree(tree = []) {
let arr = [];
const arr = [];
if (!!tree && tree.length !== 0) {
tree.forEach((item) => {
let obj = {};
const obj = {};
obj.title = item.articleCategoryName;
obj.value = item.id;
obj.attr = item.articleCategoryName; // 其他你想要添加的属性
obj.expand = false;
obj.selected = false;
obj.children = this.getTree(item.children); // 递归调用
obj.attr = item.articleCategoryName;
obj.children = this.getTree(item.children);
arr.push(obj);
});
}
return arr;
},
// 获取文章列表
getDataList(val) {
if (val) this.form = {};
this.loading = true;
@@ -409,7 +315,6 @@ export default {
this.loading = false;
if (res.success) {
this.total = res.result.total;
//为了在是否展示一列展示开关 需要改一下数据类型,最终提交再次更改
this.data = [];
if (res.result.records.length > 0) {
this.data = res.result.records;
@@ -419,13 +324,11 @@ export default {
this.total = this.data?.length;
this.loading = false;
},
// 添加文章
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalType === 0) {
// 添加 避免编辑后传入id等数据 记得删除
delete this.form.id;
saveArticle(this.form).then((res) => {
this.submitLoading = false;
@@ -436,7 +339,6 @@ export default {
}
});
} else {
// 编辑
updateArticle(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
@@ -449,30 +351,24 @@ export default {
}
});
},
// 添加文章modal
add() {
this.modalType = 0;
this.modalTitle = "添加文章";
this.treeValue = "";
this.form = {
sort: 1,
content: "",
};
this.$refs.form.resetFields();
this.$refs.form?.resetFields();
delete this.form.id;
this.modalVisible = true;
},
// 编辑文章modal
edit(data) {
this.modalType = 1;
this.modalTitle = "编辑文章";
this.treeValue = "";
this.form = {
content: "",
};
this.$refs.form.resetFields();
this.form = { content: "" };
this.$refs.form?.resetFields();
seeArticle(data.id).then((res) => {
if (res.result) {
this.modalVisible = true;
@@ -486,14 +382,12 @@ export default {
}
});
},
// 删除文章
remove(v) {
this.$Modal.confirm({
title: "确认删除",
content: "您确认要删除么?",
loading: true,
onOk: () => {
// 删除
delArticle(v.id).then((res) => {
this.$Modal.remove();
if (res.success) {

View File

@@ -1,17 +1,22 @@
<template>
<div class="search">
<Card>
<Tabs @on-click="handleClickTab">
<TabPane
<el-card>
<el-tabs v-model="currentTemplate">
<el-tab-pane
v-for="(item, index) in templatesWay"
:key="index"
:name="item.template"
:label="item.label"
:name="item.template"
>
<components v-if="item.template == currentTemplate" :is="currentTemplate"></components>
</TabPane>
</Tabs>
</Card>
<todayHotWords v-if="item.template === 'todayHotWords' && currentTemplate === 'todayHotWords'" />
<historyHotWords v-if="item.template === 'historyHotWords' && currentTemplate === 'historyHotWords'" />
<statisticsHotWords
v-if="item.template === 'statisticsHotWords' && currentTemplate === 'statisticsHotWords'"
/>
<setupHotWords v-if="item.template === 'setupHotWords' && currentTemplate === 'setupHotWords'" />
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</template>
@@ -20,52 +25,27 @@ import todayHotWords from "./template/todayHotWords";
import historyHotWords from "./template/historyHotWords";
import setupHotWords from "./template/setupHotWords";
import statisticsHotWords from "./template/statisticsHotWords";
export default {
name: "hotWords",
components: {
todayHotWords,
historyHotWords,
setupHotWords,
statisticsHotWords
statisticsHotWords,
},
data() {
return {
// 模版集合key value
templatesWay: [
{
template: "todayHotWords",
label: "今日热词",
},
{
template: "historyHotWords",
label: "历史热词",
},
{
template: "statisticsHotWords",
label: "热词统计",
},
{
template: "setupHotWords",
label: "设置热词",
},
{ template: "todayHotWords", label: "今日热词" },
{ template: "historyHotWords", label: "历史热词" },
{ template: "statisticsHotWords", label: "热词统计" },
{ template: "setupHotWords", label: "设置热词" },
],
// 引入模板
templates: {
todayHotWords,
historyHotWords,
setupHotWords,
statisticsHotWords
},
// 当前模版
currentTemplate: "todayHotWords",
};
},
methods: {
handleClickTab(val) {
this.currentTemplate = val;
},
},
};
</script>
<style lang="scss" scoped></style>

View File

@@ -1,4 +1,4 @@
::v-deep .ivu-input{
:deep(.ivu-input){
width: 200px !important;
}
@@ -16,7 +16,7 @@
margin-right: 10px;
}
::v-deep .ivu-form-item-content {
:deep(.ivu-form-item-content){
margin-left: 0 !important;
display: flex;
}

View File

@@ -1,8 +1,10 @@
import { markRaw } from "vue";
import HOTWORD_SETTING from "./HOTWORD_SETTING";
import HOTWORDS_HISTORY from "./HOTWORDS_HISTORY";
import HOTWORDS_TODAY from "./HOTWORDS_TODAY";
export default {
HOTWORD_SETTING,
HOTWORDS_TODAY,
HOTWORDS_HISTORY
HOTWORD_SETTING: markRaw(HOTWORD_SETTING),
HOTWORDS_TODAY: markRaw(HOTWORDS_TODAY),
HOTWORDS_HISTORY: markRaw(HOTWORDS_HISTORY),
};

View File

@@ -1,25 +1,21 @@
<template>
<div class="box">
<Row class="operation">
<Col span="12">
<DatePicker
:options="options"
@on-change="search"
type="date"
placement="bottom-end"
placeholder="选择查看日期"
style="width: 200px"
v-model="yestDate"
></DatePicker>
</Col>
</Row>
<Row>
<p>
<Alert type="success">
这里展示历史某一天的热词数据统计,可根据需求配置每日持久化多少条数据。
</Alert>
</p>
</Row>
<el-date-picker
v-model="params.date"
type="date"
placeholder="选择查看日期"
value-format="YYYY-MM-DD"
:disabled-date="disabledDate"
style="width: 200px"
@change="search"
/>
<el-alert
class="mt_10"
type="success"
:closable="false"
show-icon
title="这里展示历史某一天的热词数据统计,可根据需求配置每日持久化多少条数据。"
/>
<div id="container"></div>
</div>
@@ -28,64 +24,53 @@
<script>
import { Chart } from "@antv/g2";
import { getHotWordsHistory } from "@/api/index";
import affixTime from "@/components/affix-time";
import { Message } from "view-design";
import { Message } from "@/utils/message";
export default {
components: {
affixTime,
},
data() {
return {
yestDate:new Date(new Date().getTime() - 24 * 60 * 60 * 1000).toString(),
yestDate:this.$options.filters.unixToDate(
(new Date().getTime() / 1000) - (24 * 60 * 60),
"yyyy年MM月dd日"
),
params: {
date: this.$options.filters.unixToDate(
(new Date().getTime() / 1000) - (24 * 60 * 60),
"yyyy-MM-dd"
),
},
hotWordsChart: "", //图表
hotWordsData: [], //数据
options: {
disabledDate: (date) => {
if (this.endMonth) {
let endDate = this.getDate(this.endMonth);
return (date && date > endDate) || date > new Date();
} else {
return date && date > new Date();
}
},
date: "",
},
hotWordsChart: null,
hotWordsData: [],
endMonth: null,
};
},
computed: {},
created() {
const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
this.params.date = this.$filters.unixToDate(Math.floor(yesterday.getTime() / 1000), "yyyy-MM-dd");
},
methods: {
clickBreadcrumb(val) {
this.params = { ...this.params, ...val };
disabledDate(date) {
if (this.endMonth) {
const endDate = this.getDate(this.endMonth);
return (date && date > endDate) || date > new Date();
}
return date && date > new Date();
},
getDate(monthStr) {
const [year, month] = monthStr.split("-");
return new Date(year, month, 0);
},
// 初始化图表
async search(val) {
console.log(val)
val ? (this.params.date = val) : "";
if (val) {
this.params.date = val;
}
const res = await getHotWordsHistory(this.params);
if (res.success) {
this.hotWordsData = res.result;
this.hotWordsChart.data(this.hotWordsData);
this.hotWordsChart.render();
if (!this.hotWordsData) {
this.hotWordsData = res.result || [];
if (this.hotWordsChart) {
this.hotWordsChart.changeData(this.hotWordsData);
this.hotWordsChart.render();
}
if (!this.hotWordsData.length) {
Message.error("暂无数据");
}
}
},
handleClickSearch() {},
init() {
let chart = this.hotWordsChart;
chart = new Chart({
const chart = new Chart({
container: "container",
autoFit: true,
height: 500,
@@ -94,14 +79,12 @@ export default {
chart.scale("score", {
alias: "搜索次数",
});
chart.axis("keywords", {
tickLine: {
alignTick: false,
},
});
chart.axis("score", false);
chart.tooltip({
showMarkers: false,
});
@@ -118,12 +101,6 @@ export default {
</script>
<style lang="scss" scoped>
.affix-time {
padding-left: 15px;
}
.box {
min-height: 400px;
margin-left: 50px;

View File

@@ -1,13 +1,13 @@
<template>
<div class="layout">
<Form
<el-form
ref="formValidate"
:label-width="150"
label-width="150px"
label-position="right"
:model="formValidate"
:rules="ruleValidate"
>
<FormItem label="热词默认配置" prop="hotWordsSettingItems">
<el-form-item label="热词默认配置" prop="hotWordsSettingItems">
<div
class="item-label"
v-for="(item, index) in formValidate.hotWordsSettingItems"
@@ -16,55 +16,55 @@
<div>
<div class="item-keyword">
<div>热词:</div>
<Input type="text" v-model="item.keywords"> </Input>
<el-input v-model="item.keywords" />
</div>
<div class="item-score">
<div>分数:</div>
<InputNumber :max="5" :min="0" v-model="item.score"></InputNumber>
<el-input-number :max="5" :min="0" v-model="item.score" />
</div>
</div>
<div>
<Button type="primary" @click="formValidate.hotWordsSettingItems.splice(index,1)">删除</Button>
<el-button type="primary" @click="formValidate.hotWordsSettingItems.splice(index, 1)">
删除
</el-button>
</div>
</div>
<Button @click="addSetItem">添加配置</Button>
</FormItem>
<FormItem label="每日持久化热词数量" prop="saveNum">
<InputNumber :min="0" v-model="formValidate.saveNum" />
</FormItem>
<el-button @click="addSetItem">添加配置</el-button>
</el-form-item>
<el-form-item label="每日持久化热词数量" prop="saveNum">
<el-input-number :min="0" v-model="formValidate.saveNum" />
</el-form-item>
<div class="label-btns">
<Button type="primary" @click="submit('formValidate')">保存</Button>
<el-button type="primary" @click="submit('formValidate')">保存</el-button>
</div>
</Form>
</el-form>
</div>
</template>
<script>
import { setSetting, getSetting } from "@/api/index";
export default {
data() {
return {
ruleValidate: {}, // 验证规则
ruleValidate: {},
formValidate: {
// 表单数据
saveNum: 1, // 每日保存数量
saveNum: 1,
hotWordsSettingItems: [
{
keywords: "",
score: 1,
},
], // 热词默认配置
],
},
};
},
created() {
this.init();
},
methods: {
// 添加热词默认配置
addSetItem(val) {
addSetItem() {
if (this.formValidate.hotWordsSettingItems.length >= 5) {
this.$Message.error("最多5个热词项");
} else {
@@ -74,18 +74,15 @@ export default {
});
}
},
submit(name) {
this.$refs["formValidate"].validate(async (valid) => {
this.$refs[name].validate((valid) => {
if (valid) {
this.setupSetting();
} else {
that.$Message.error("请正确填写内容!");
return flag;
this.$Message.error("请正确填写内容!");
}
});
},
// 保存设置
setupSetting() {
setSetting("HOT_WORDS", this.formValidate).then((res) => {
if (res.success) {
@@ -96,10 +93,9 @@ export default {
this.init();
});
},
// 实例化数据
async init() {
const res = await getSetting("HOT_WORDS");
if (res.success) {
if (res.success && res.result) {
this.formValidate = res.result;
}
},
@@ -113,23 +109,22 @@ export default {
margin-bottom: 10px;
display: flex;
align-items: center;
width:500px;
justify-content:space-between;
}
::v-deep .ivu-input {
width: 100px !important;
}
.ivu-input-wrapper {
width: 300px;
margin-right: 10px;
width: 500px;
justify-content: space-between;
}
.item-keyword,
.item-score {
display: flex;
align-items: center;
margin-bottom: 10px;
> div {
margin-right: 20px;
}
}
.item-keyword .el-input {
width: 200px;
}
</style>

View File

@@ -1,23 +1,23 @@
<template>
<div>
<div class="affix-time">
<Affix :offset-top="100">
<div class="flex affix-box">
<affixTime :closeShop="true" @selected="clickBreadcrumb" />
<InputNumber
placeholder="展示前N"
:max="200"
:min="10"
v-model="params.top"
></InputNumber>
<Button style="margin-left: 10px" @click="search">搜索</Button>
</div>
</Affix>
</div>
<div class="affix-time">
<el-affix :offset="100">
<div class="flex affix-box">
<affixTime :closeShop="true" @selected="clickBreadcrumb" />
<el-input-number
v-model="params.top"
placeholder="展示前N"
:max="200"
:min="10"
/>
<el-button style="margin-left: 10px" @click="search">搜索</el-button>
</div>
</el-affix>
<div id="container"></div>
<Table border :columns="columns" :data="hotWordsData"></Table>
<el-table border :data="hotWordsData" style="width: 100%">
<el-table-column prop="keywords" label="热词名称" min-width="160" />
<el-table-column prop="score" label="搜索次数" min-width="120" />
</el-table>
</div>
</template>
@@ -33,31 +33,18 @@ export default {
data() {
return {
params: {
// 请求参数
searchType: "LAST_SEVEN",
year: "",
month: "",
top: 50,
},
columns: [
{
title: "热词名称",
key: "keywords",
},
{
title: "搜索次数",
key: "score",
},
],
hotWordsChart: "", //图表
hotWordsData: [], //数据
orderChart: "",
hotWordsChart: "",
hotWordsData: [],
};
},
computed: {},
watch: {
params: {
handler(val) {
handler() {
this.search();
},
deep: true,
@@ -71,7 +58,6 @@ export default {
clickBreadcrumb(val) {
this.params = { ...this.params, ...val };
},
// 初始化图表
async search() {
const res = await getHotWordsStatistics(this.params);
if (res.success) {
@@ -84,11 +70,9 @@ export default {
padding: [50, 20, 50, 20],
});
}
this.init();
}
},
handleClickSearch() {},
init() {
if (this.hotWordsChart) {
this.hotWordsChart.data(this.hotWordsData);
@@ -106,26 +90,9 @@ export default {
this.hotWordsChart.tooltip({
showMarkers: false,
});
this.hotWordsChart
.interval()
.position("keywords*score")
.color("#f59b99");
this.hotWordsChart.interval().position("keywords*score").color("#f59b99");
this.hotWordsChart.interaction("element-active");
// 添加文本标注
// this.hotWordsData.forEach((item) => {
// this.hotWordsChart
// .annotation()
// .text({
// position: [item.keywords, item.score],
// content: item.score,
// style: {
// textAlign: "center",
// },
// offsetY: -30,
// })
// });
this.hotWordsChart.render();
}
},

View File

@@ -1,72 +1,67 @@
<template>
<div>
<Row class="operation">
<Button @click="add()" type="primary">设置今日热词</Button>
</Row>
<Row>
<p>
<Alert type="success">
这里展示今日系统中搜索前一百的搜索热词,分数为热词在排序系统中的分数,分数越高,可以在用户获取热词时进行优先展示(首页商品搜索栏下方推荐位)(分数可以填写负数,会降低推荐度)
</Alert>
</p>
</Row>
<div class="operation mb_10">
<el-button type="primary" @click="add()">设置今日热词</el-button>
</div>
<el-alert
type="success"
:closable="false"
show-icon
title="这里展示今日系统中搜索前一百的搜索热词,分数为热词在排序系统中的分数,分数越高,可以在用户获取热词时进行优先展示(首页商品搜索栏下方推荐位)(分数可以填写负数,会降低推荐度)"
/>
<div class="card-list">
<Card v-for="words in data" class="card-item" :key="words" onclick="">
<p>
<a href="#" slot="extra" @click.prevent="add(words)">{{ words }}</a>
</p>
<p slot="extra">
<a @click="deleteWords(words)">
<Icon type="md-close" />
</a>
</p>
</Card>
<el-card v-for="words in data" :key="words" class="card-item" shadow="hover">
<div class="card-item-inner">
<a href="#" class="word-link" @click.prevent="add(words)">{{ words }}</a>
<el-icon class="close-icon" @click="deleteWords(words)"><Close /></el-icon>
</div>
</el-card>
</div>
<Modal
:title="modalTitle"
<el-dialog
v-model="modalVisible"
:mask-closable="false"
:width="500"
:title="modalTitle"
width="500px"
:close-on-click-modal="false"
append-to-body
destroy-on-close
>
<Form ref="form" :model="form" :label-width="100" :rules="formValidate">
<FormItem label="热词" prop="keywords">
<Input v-model="form.keywords" clearable style="width: 100%" />
</FormItem>
<FormItem label="分数" prop="point">
<InputNumber v-model="form.point" clearable style="width: 100%" />
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="modalVisible = false">取消</Button>
<Button type="primary" :loading="submitLoading" @click="handleSubmit"
>提交</Button
>
</div>
</Modal>
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="热词" prop="keywords">
<el-input v-model="form.keywords" clearable style="width: 100%" />
</el-form-item>
<el-form-item label="分数" prop="point">
<el-input-number v-model="form.point" style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="modalVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">提交</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import { Close } from "@element-plus/icons-vue";
import { getHotWords, setHotWords, deleteHotWords } from "@/api/index";
import { regular } from "@/utils";
export default {
name: "hotWords",
components: {},
name: "todayHotWords",
components: { Close },
data() {
return {
submitLoading: false,
modalTitle: "",
loading: true, // 表单加载状态
modalVisible: false, //弹出框
loading: true,
modalVisible: false,
form: {
keywords: "",
point: 0,
},
data: [], // 表单数据
// 表单验证规则
data: [],
formValidate: {
keywords: [regular.REQUIRED, regular.VARCHAR20],
point: [regular.REQUIRED, regular.NUMBER],
@@ -77,20 +72,15 @@ export default {
init() {
this.getDataList();
},
handleClickTab(val) {
console.log(val);
},
getDataList() {
this.loading = true;
getHotWords().then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result;
this.$store.state.app.hotWords = this.data
this.$store.state.app.hotWords = this.data;
}
});
this.loading = false;
},
handleSubmit() {
this.$refs.form.validate((valid) => {
@@ -107,26 +97,20 @@ export default {
}
});
},
//设置热词优先
add(words) {
this.modalType = 0;
this.modalTitle = "设置热词";
if (words) {
this.form.keywords = words;
} else {
this.form.keywords = "";
}
this.form.keywords = words || "";
this.form.point = 1;
this.modalVisible = true;
},
deleteWords(words) {
this.$Modal.confirm({
title: "是否确定删除热词",
content: "<p>您确定要删除此热词吗?</p>",
okText: "确实",
content: "您确定要删除此热词吗?",
okText: "确定",
cancelText: "取消",
onOk: () => {
deleteHotWords({words}).then((res) => {
deleteHotWords({ words }).then((res) => {
if (res.success) {
this.$Message.success("删除成功");
this.getDataList();
@@ -141,22 +125,38 @@ export default {
},
};
</script>
<style lang="scss" scoped>
.card-list {
display: flex;
flex-wrap: wrap;
align-items: center;
margin-top: 16px;
}
.card-item {
min-width: 100px;
margin: 20px;
}
</style>
<style>
.ivu-card-extra {
right: 3px;
top: 15px;
z-index: 99;
margin: 10px;
}
.card-item-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
}
.word-link {
color: var(--el-color-primary);
text-decoration: none;
}
.close-icon {
cursor: pointer;
color: var(--el-text-color-secondary);
&:hover {
color: var(--el-color-danger);
}
}
</style>

View File

@@ -1,205 +1,148 @@
<template>
<div class="search">
<Card>
<Table
:loading="loading"
border
:columns="columns"
:data="data"
<el-card>
<el-table
ref="table"
></Table>
<Row type="flex" justify="end" class="mt_10">
<Page
:current="searchForm.pageNumber"
:total="total"
:page-size="searchForm.pageSize"
@on-change="changePage"
@on-page-size-change="changePageSize"
:page-size-opts="[20, 50, 100]"
size="small"
show-total
show-elevator
show-sizer
></Page>
</Row>
</Card>
<!-- 修改模态框 -->
<Modal v-model="formValidate" title="详细信息" width="500">
<Form
ref="formValidate"
:model="form"
:label-width="80"
v-loading="loading"
border
:data="data"
style="width: 100%"
>
<FormItem label="用户名" prop="userName">
<span> {{form.userName}}</span>
</FormItem>
<FormItem label="手机号码" prop="mobile">
<span> {{form.mobile}}</span>
</FormItem>
<FormItem label="类型" prop="type">
<el-table-column prop="userName" label="会员名称" min-width="120" show-overflow-tooltip />
<el-table-column prop="mobile" label="手机号码" min-width="120" show-overflow-tooltip />
<el-table-column prop="context" label="反馈内容" min-width="380" show-overflow-tooltip />
<el-table-column label="类型" min-width="120">
<template #default="{ row }">
<span v-if="row">
<span v-if="row.type == 'FUNCTION'">功能建议</span>
<span v-else-if="row.type == 'OPTIMIZE'">优化反馈</span>
<span v-else-if="row.type == 'OTHER'">其他意见</span>
<span v-else>未知意见</span>
</span>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="170" sortable />
<el-table-column label="操作" width="130" align="center">
<template #default="{ row }">
<div v-if="row" class="ops">
<a class="link-text" @click="detail(row)">查看</a>
</div>
</template>
</el-table-column>
</el-table>
<div class="mt_10" style="display: flex; justify-content: flex-end">
<el-pagination
v-model:current-page="searchForm.pageNumber"
v-model:page-size="searchForm.pageSize"
:page-sizes="[20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
size="small"
@current-change="changePage"
@size-change="changePageSize"
/>
</div>
</el-card>
<el-dialog v-model="detailVisible" title="详细信息" width="500px" destroy-on-close>
<el-form ref="formValidate" :model="form" label-width="80px">
<el-form-item label="用户名" prop="userName">
<span>{{ form.userName }}</span>
</el-form-item>
<el-form-item label="手机号码" prop="mobile">
<span>{{ form.mobile }}</span>
</el-form-item>
<el-form-item label="类型" prop="type">
<span v-if="form.type == 'FUNCTION'">功能建议</span>
<span v-if="form.type == 'OPTIMIZE'">优化反馈</span>
<span v-if="form.type == 'OTHER'">其他意见</span>
</FormItem>
<FormItem label="反馈内容" prop="context">
<Input style="width: 85%" v-model="form.context" type="textarea" disabled :autosize="{minRows: 3,maxRows: 5}"
></Input>
</FormItem>
<FormItem label="相关材料" prop="images">
<div v-if="form.images == null">
暂无
</div>
</el-form-item>
<el-form-item label="反馈内容" prop="context">
<el-input
v-model="form.context"
type="textarea"
disabled
:autosize="{ minRows: 3, maxRows: 5 }"
style="width: 85%"
/>
</el-form-item>
<el-form-item label="相关材料" prop="images">
<div v-if="form.images == null">暂无</div>
<div v-else>
<span v-for="(item, index) in this.form.images.split(',')" :key="index">
<Avatar shape="square" icon="ios-person" size="large" style="width: 80px;height: 90px;margin-right: 5px" :src="item"
/>
</span>
<el-avatar
v-for="(item, index) in form.images.split(',')"
:key="index"
shape="square"
:size="80"
:src="item"
style="margin-right: 5px"
/>
</div>
</FormItem>
</Form>
<div slot="footer">
<Button type="text" @click="formValidate = false">取消</Button>
</div>
</Modal>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="detailVisible = false">取消</el-button>
</template>
</el-dialog>
</div>
</template>
<script>
import {
getMemberFeedback,
getMemberFeedbackDetail
} from "@/api/other";
import { getMemberFeedback, getMemberFeedbackDetail } from "@/api/other";
export default {
name: "feedback",
data() {
return {
loading: true, // 加载状态
form: {}, // 表单数据
searchForm: { // 请求参数
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
},
formValidate: false, // modal显隐
columns: [ // 表头
{
title: "会员名称",
key: "userName",
minWidth: 120,
tooltip: true
},
{
title: "手机号码",
key: "mobile",
minWidth: 120,
tooltip: true
},
{
title: "反馈内容",
key: "context",
minWidth: 380,
tooltip: true
},
{
title: "类型",
key: "type",
minWidth: 120,
render: (h, params) => {
if (params.row.type == "FUNCTION") {
return h('div', [h('span', {}, "功能建议")]);
} else if (params.row.type == "OPTIMIZE") {
return h('div', [h('span', {}, "优化反馈")]);
} else if (params.row.type == "OTHER") {
return h('div', [h('span', {}, "其他意见")]);
} else {
return h('div', [h('span', {}, "未知意见")]);
}
}
},
{
title: "创建时间",
key: "createTime",
width: 170,
sortable: true,
},
{
title: "操作",
key: "action",
align: "center",
width: 130,
render: (h, params) => {
return h("div", { class: "ops" }, [
h(
"a",
{
on: {
click: () => {
this.detail(params.row);
},
},
},
"查看"
)
]);
},
},
],
data: [], // 表格数据
total: 0 // 数据总数
};
export default {
name: "feedback",
data() {
return {
loading: true,
form: {},
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
},
detailVisible: false,
data: [],
total: 0,
};
},
methods: {
init() {
this.getFeedback();
},
methods: {
// 初始化数据
init() {
this.getFeedback();
},
// 分页 修改页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getFeedback();
},
// 分页 修改页数
changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getFeedback();
},
/**
* 查询意见反馈
*/
getFeedback() {
this.loading = true;
getMemberFeedback(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
},
/**
* 投诉建议详细
*/
detail(v) {
getMemberFeedbackDetail(v.id).then((res) => {
this.loading = false;
if (res.success) {
this.$set(this, "form", res.result);
this.formValidate = true
}
});
}
changePage() {
this.getFeedback();
},
mounted() {
this.init();
changePageSize() {
this.searchForm.pageNumber = 1;
this.getFeedback();
},
};
getFeedback() {
this.loading = true;
getMemberFeedback(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
},
detail(v) {
getMemberFeedbackDetail(v.id).then((res) => {
this.loading = false;
if (res.success) {
this.form = res.result;
this.detailVisible = true;
}
});
},
},
mounted() {
this.init();
},
};
</script>
<style lang="scss" scoped>
.ops a {