Commit 7af18ac1 by caoyanzhi

Merge branch 'feature/3月迭代'

parents 259c117a 876803e6
......@@ -12,12 +12,13 @@
"dependencies": {
"@babel/plugin-proposal-optional-chaining": "^7.11.0",
"axios": "^0.19.2",
"vue-clipboard2": "^0.1.1",
"core-js": "^2.6.5",
"element-ui": "^2.13.0",
"js-cookie": "^2.2.0",
"vue": "2.6.6",
"vue-router": "^3.1.5"
"vue-clipboard2": "^0.1.1",
"vue-router": "^3.1.5",
"vuedraggable": "^2.24.3"
},
"devDependencies": {
"@commitlint/cli": "^11.0.0",
......
......@@ -7,6 +7,8 @@
<link rel="icon" href="<%= BASE_URL %>favicon2.ico">
<link rel="stylesheet" type="text/css" href="//web-1251519181.file.myqcloud.com/custom-element/custom-element.1.0.68.css" />
<link rel="stylesheet" href="//at.alicdn.com/t/font_2995156_rf810o69kh.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_2859043_3i96mi4mdy2.css">
<title>实施运维</title>
</head>
<body class="damolish">
......@@ -24,5 +26,7 @@
})();
</script>
<script src="//web-1251519181.file.myqcloud.com/components/user-info.1.0.3.js"></script>
<script src="//web-1251519181.file.myqcloud.com/components/pagination.1.0.8.js"></script>
<!-- <script src="//web-1251519181.file.myqcloud.com/components/upload-image.2.0.30.js"></script> -->
</body>
</html>
......@@ -6,33 +6,44 @@ let marketingApi = {
// 获取模板管理列表
getTemplateList: {
method: 'post',
url: '/sms/page-sms-setting'
url: '/sms/page-sms-setting',
useFormData: true
},
// 获取模板管理列表详情表格
getTemplateDetailList: {
method: 'post',
url: '/sms/page-template',
useFormData: true
},
// 提审模板
sendExamine: {
method: 'post',
url: '/sms/audit-template',
useFormData: true
},
// 获取通道详情
getSmsSetting: {
method: 'post',
url: '/sms/sms-setting-detail',
// useFormData: true
useFormData: true
},
// 修改通道
updateSms: {
method: 'post',
url: '/sms/update-sms-setting',
// useFormData: true
useFormData: true
},
getSystemTemplate: '/sms/page-system-template',
// 生成appid
getAppid: '/sms/default-appid',
activityGetList: '/operation/activity/getList',
activityCategoryGetList: '/operation/activity/category/getList',
activityRightMenuList: '/operation/activity/rightMenuList',
activityUpdate: '/operation/activity/update',
activityCategoryUpdate: {
method: 'post',
url: '/operation/activity/category/addOrUpdate'
}
};
let authApi = {
......
......@@ -44,7 +44,6 @@ export default {
methods: {
getMenuList() {
getUserInfo().then(res => {
console.log(res);
this.entranceList = [];
let entranceList = res.result.menuList ? generatorMenuList(res.result.menuList) : [];
console.log(entranceList);
......
......@@ -29,5 +29,17 @@ export const routes = [
component: _import('message', 'system-message')
},
]
},
{
path: '/activity',
name: '营销管理',
component: Layout,
children: [
{
path: 'activity-config',
name: '营销活动配置',
component: _import('activity', 'index')
}
]
}
];
......@@ -6,7 +6,7 @@ import { origin } from '@/config/index.js';
axios.defaults.baseURL = origin;
const request = (opt, params) => {
params = Object.assign({}, params);
params = typeof params === 'object' ? JSON.parse(JSON.stringify(params)) : params;
opt = Object.assign({}, opt);
let requestConfig = {
// 请求路径
......@@ -29,7 +29,7 @@ const request = (opt, params) => {
requestConfig.params = params;
break;
case 'post':
requestConfig.data = qs.stringify(params);
requestConfig.data = params;
break;
default:
requestConfig.data = params;
......
<template>
<el-dialog custom-class="category-dialog" :visible.sync=" dialogVisible" :rules="rules" class="category-dialog-wrap"
width="600px" :close-on-click-modal="false" :close-on-press-escape="false">
<div v-loading="loading">
<el-row type="flex" justify="space-between">
<h1 class="el-dialog__title">分类管理</h1>
<i class="iconfont-components3 icon-cp-close" @click="handleClose"></i>
</el-row>
<el-form ref="form" :model="list">
<draggable tag="ul" :list="list" class="list-group" handle=".handle">
<li class="list-group-item" v-for="(item, idx) in list" :key="idx">
<el-form-item :prop="idx+'.categoryName'" :rules="rules.categoryName">
<!-- <p class="show-name" v-show="!item.isEdit">{{list[idx].categoryName}}</p> -->
<el-input v-model="list[idx].categoryName"
placeholder="请输入分类名称,不超过 8 个字"
maxlength="8"
style="width:466px;" @change="saveEdit(idx)">
</el-input>
</el-form-item>
<div class="btn-wrap">
<!-- <template v-if="item.isEdit">
<el-button type="text" @click="cancelEdit(idx)" style="color:#303133;">取消</el-button>
<el-button type="text" @click="saveEdit(idx)">保存</el-button>
</template> -->
<!-- <template v-else> -->
<!-- <span class="icon-span" @click="editItem(idx)"><i
class="iconfont-components3 icon-cp-edit-outlined" /></span> -->
<span class="icon-span" @click="onDeleteItem(idx)"><i
class="iconfont-components3 icon-cp-DeleteOutlined" /></span>
<el-tooltip popper-class="category-dialog-tooltip" effect="dark" content="拖拽排序"
placement="top">
<i class="iconfont-components3 icon-cp-tuozhuaipaixu handle"></i>
</el-tooltip>
<!-- </template> -->
</div>
</li>
</draggable>
</el-form>
<el-button type="text" @click="addItem"><i
class="iconfont-components3 icon-cp-PlusOutlined" />
添加分类</el-button>
<div class="el-dialog__footer">
<el-button @click="handleClose">取 消</el-button>
<el-button type="primary" @click="submit">确 定</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import requestApi from '@/api/operation';
const { activityGetList, activityCategoryGetList, activityCategoryUpdate } = requestApi;
import draggable from 'vuedraggable';
export default {
name: 'CategoryDialog',
components: {
draggable
},
props: {
visible: {
type: Boolean,
default: false
},
},
data() {
const nameValidate = (r, v, c) => {
if (!v) c(new Error('请填写分类名称'));
if (this.nameMap[ v ] === (this.list.indexOf(el => el.categoryName === v))) c(new Error(`${v}分类名称重复`));
c();
};
return {
dialogVisible: false,
loading: false,
list: [],
nameMap: {},
defaultItem: {
activityCategoryId: '',
categoryName: '',
isEdit: true,
},
rules: {
categoryName: [ { required: true, validator: nameValidate } ],
},
copyItem: {}
};
},
methods: {
handleClose() {
this.$nextTick(_ => {
this.list = [];
this.$emit('update:visible', false);
});
},
async getCategoryList() {
this.loading = true;
let { result = [] } = await activityCategoryGetList().finally(_ => this.loading = false);
let nameMap = {};
result = result.map((el, idx) => {
el.isEdit = false;
nameMap[ el.categoryName ] = idx;
return el;
});
this.nameMap = nameMap;
this.list = JSON.parse(JSON.stringify(result));
},
submit() {
this.loading = true;
this.$refs.form.validate(async valid => {
if (valid) {
let list = [];
list = this.list.map((el, idx) => {
el = {
categorySort: idx,
categoryName: el.categoryName,
activityCategoryId: el.activityCategoryId || ''
};
return el;
});
const { code } = await activityCategoryUpdate(list).finally(() => this.loading = false);
if (code === '0') this.$message.success('提交成功');
this.handleClose();
this.$emit('refresh');
} else {
this.loading = false;
}
});
},
async onDeleteItem(idx) {
// activityGetList
if (this.list[ idx ].activityCategoryId) {
const { result = [] } = await activityGetList({ activityCategoryId: this.list[ idx ].activityCategoryId });
if (Array.isArray(result) && result.length) {
return this.$message.error('该分类有活动在引用,不可删除!');
}
}
this.list.splice(idx, 1);
delete this.nameMap[ this.list[ idx ].categoryName ];
},
editItem(idx) {
this.copyItem = { ...this.list[ idx ] };
this.list[ idx ].isEdit = true;
},
saveEdit(idx) {
this.$refs.form.validateField(idx + '.categoryName', errorMessage => {
if (errorMessage) return;
this.$set(this.list, idx, { ...this.list[ idx ], isEdit: false });
this.nameMap[ this.list[ idx ].categoryName ] = idx;
});
},
async cancelEdit(idx) {
if (!Object.keys(this.copyItem).length) return this.list.splice(idx, 1);
this.$set(this.list, idx, { ...this.copyItem, isEdit: false });
this.copyItem = {};
},
addItem() {
this.copyItem = {};
this.list.push({ ...this.defaultItem });
}
},
watch: {
visible: {
handler: function(n, o) {
this.dialogVisible = n;
if (n) {
this.getCategoryList();
}
},
immediate: true
},
},
};
</script>
<style lang="less" scoped>
.list-group {
max-height: 516px;
padding-bottom: 20px;
overflow-y: auto;
}
.list-group-item {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 10px;
& + .list-group-item {
margin-top: 20px;
}
.el-form-item {
margin-bottom: 0;
.show-name{
width: 430px;
height: 32px;
padding-left: 11px;
color: #303133;
font-size: 14px;
line-height: 32px;
background: #F2F3F7;
border-radius: 2px;
cursor: default;
}
}
.icon-span {
display: inline-block;
width: 24px;
height: 24px;
line-height: 24px;
text-align: center;
cursor: pointer;
&:hover {
background: #f2f3f7;
border-radius: 2px;
}
&+.icon-span{
margin-left: 4px;
}
i.iconfont-components3{
color: #303133;
}
}
.handle.icon-cp-tuozhuaipaixu {
margin-left: 4px;
&:hover {
cursor: move;
color: #2f54eb;
}
}
}
</style>
<style lang="less">
.category-dialog-wrap{
display: flex;
align-items: center;
justify-content: center;
}
.category-dialog {
margin: 0 !important;
padding: 0 4px 4px;
max-height: 700px;
.el-dialog__header {
display: none;
}
.el-dialog__body {
padding-bottom: 0 !important;
.el-row {
margin-bottom: 24px;
.icon-cp-close {
cursor: pointer;
&:hover {
color: #2f54eb;
}
}
}
}
.el-dialog__footer {
padding: 20px 0;
}
}
.el-tooltip__popper.category-dialog-tooltip {
line-height: 22px;
padding: 0 5px;
font-size: 12px;
transform: translateY(4px);
.popper__arrow {
display: none;
}
}
</style>
\ No newline at end of file
<template>
<el-dialog custom-class="category-dialog" :visible.sync=" dialogVisible" :rules="rules" class="category-dialog-wrap"
width="600px" :close-on-click-modal="false" :close-on-press-escape="false">
<div v-loading="loading">
<el-row type="flex" justify="space-between">
<h1 class="el-dialog__title">编辑活动</h1>
<i class="iconfont-components3 icon-cp-close" @click="handleClose"></i>
</el-row>
<div class="tip-message-wrap">
<i class="iconfont-components3 icon-cp-warning-circle-fill"></i>
<div class="tip-message-content">活动新增成功,需要给用户配置活动权限后,活动才会展示在GIC后台-营销活动列表</div>
</div>
<el-form ref="form" :model="form" label-width="92px" :rules="rules">
<el-form-item label="活动分类" prop="activityCategoryId">
<el-select v-model="form.activityCategoryId" placeholder="请选择" style="width:460px;">
<el-option v-for="item in categoryList" :key="item.activityCategoryId"
:value="item.activityCategoryId" :label="item.categoryName" />
</el-select>
</el-form-item>
<el-form-item label="活动名称" prop="activityInfo">
<el-select :value="form.activityInfo" disabled placeholder="请选择" style="width:460px;"
value-key="activityCode">
<el-option v-for="item in activityList" :key="item.activityCode"
:label="item.activityName" :value="item">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="活动说明" prop="activityExplain">
<el-input v-model="form.activityExplain" type="text" maxlength="20"
placeholder="请输入活动说明,不超过 20 个字"></el-input>
</el-form-item>
<el-form-item label="标签文案" prop="activityRemark">
<el-input v-model="form.activityRemark" type="text" maxlength="2"
placeholder="请输入活动说明,不超过 2 个字">
</el-input>
</el-form-item>
<el-form-item label="排序号" prop="activitySort">
<el-input-number v-model="form.activitySort" :max="1000" :min="1" :precision="0"
controls-position="right" placeholder="请输入排序号,1-1000" style="width:460px;" />
</el-form-item>
<el-form-item label="活动图片" prop="activityImage">
<vue-gic-upload-image :project-name="projectName" :action-url="actionUrl" :limit-w="80"
:limit-h="80" :image-list="imageList" :max-image-length="maxlength" :accept="accept"
:before-upload="beforeUpload" @uploadOnSuccess="uploadOnSuccess"
@deleteImage="deleteImage"
preview-append-to-body>
</vue-gic-upload-image>
<p class="image-tip">图片尺寸80*80px,格式 jpg/jpeg/png,大小 100KB 以内。</p>
</el-form-item>
</el-form>
<div class="el-dialog__footer">
<el-button @click="handleClose">取 消</el-button>
<el-button type="primary" @click="submit">确 定</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import requestApi from '@/api/operation';
const { activityCategoryGetList, activityRightMenuList, activityUpdate } = requestApi;
export default {
name: 'NewActivity',
props: {
visible: {
type: Boolean,
default: false
},
item: {
type: Object,
default() {
return {
activityConfigId: '',
};
}
},
},
data() {
const activityInfoValidator = (r, v, c) => {
console.log(v);
if (!v.activityCode || !v.activityName) {
c(new Error('请选择活动名称'));
}
c();
};
return {
dialogVisible: false,
loading: false,
form: {
activityConfigId: '',
activityCategoryId: '',
activityInfo: {},
activityCode: '',
activityName: '',
activityExplain: '',
activityRemark: '',
activityImage: '',
activitySort: undefined
},
rules: {
activityConfigId: [ { required: true, message: '请选择活动分类' } ],
activityInfo: [ { required: true, validator: activityInfoValidator } ],
activityExplain: [ { required: true, message: '请输入活动说明' } ],
activityImage: [ { required: true, message: '请上传活动图片' } ],
activitySort: [ { required: true, message: '请输入排序号' } ],
},
projectName: 'marketing-operation', // 当前项目名(必传参数)
limitW: 80, // 上传图片的限制宽度(数字),(可选参数),各个调用地方可能不同
limitH: 80, // 上传图片的限制高度(数字),(可选参数),各个调用地方可能不同
actionUrl: '/marketing-operation/upload-img', // 必选参数,上传的相对地址 String 类型,切勿硬编码写死地址
accept: 'image/jpg,image/jpeg,image/png',
maxlength: 1, // 图片数量 默认 5
imageList: [], // 是否显示已上传文件列表
categoryList: [],
activityList: [],
};
},
methods: {
handleClose() {
this.$refs.form.resetFields();
this.$nextTick(_ => {
this.$emit('update:visible', false);
});
},
init() {
this.loading = true;
this.getCategoryList();
this.getSelectActivityList();
this.loading = false;
},
async getCategoryList() {
let { result = [] } = await activityCategoryGetList();
this.categoryList = JSON.parse(JSON.stringify(result));
},
async getSelectActivityList() {
let { result = [] } = await activityRightMenuList();
this.activityList = result.map(el => {
el = { activityCode: el.menuCode, activityName: el.menuName };
return el;
});
},
submit() {
this.loading = true;
this.$refs.form.validate(async valid => {
if (valid) {
let param = {
activityConfigId: this.form.activityConfigId,
activityCategoryId: this.form.activityCategoryId,
activityCode: this.form.activityCode,
activityName: this.form.activityName,
activityExplain: this.form.activityExplain,
activityRemark: this.form.activityRemark,
activitySort: this.form.activitySort,
activityImage: this.form.activityImage,
categoryName: this.form.categoryName,
};
delete param.activityInfo;
const { code } = await activityUpdate(param).finally(() => this.loading = false);
if (code === '0') this.$message.success('提交成功');
this.handleClose();
this.$emit('refresh');
} else {
this.loading = false;
}
});
},
beforeUpload(file) {
if (file.size / 1024 > 100) {
this.$message.error('图片不能大于100kb,请重新上传!');
return false;
}
return true;
},
uploadOnSuccess({ res }) {
this.form.activityImage = res.result[ 0 ].qcloudImageUrl || '';
this.imageList = [ { url: res.result[ 0 ].qcloudImageUrl } ];
},
deleteImage() {
this.form.activityImage = '';
this.imageList = [];
},
},
watch: {
visible: {
handler: function(n, o) {
this.dialogVisible = n;
if (n) {
this.init();
this.form = Object.assign({}, this.form, this.item, { activityInfo: { activityCode: this.item.activityCode, activityName: this.item.activityName } });
this.imageList = this.item.activityImage ? [ { url: this.item.activityImage } ] : [];
}
},
immediate: true
},
},
};
</script>
<style lang="less" scoped>
.tip-message-wrap {
display: flex;
align-items: center;
height: 32px;
padding-left: 17px;
margin-bottom: 20px;
background: #f7f8fa;
border-radius: 2px;
cursor: default;
.icon-cp-warning-circle-fill {
margin-right: 5px;
font-size: 13px;
color: #2f54eb;
}
.tip-message-content {
font-size: 12px;
color: #303133;
}
}
.image-tip {
margin: 6px 0 14px;
font-size: 12px;
color: #909399;
line-height: 17px;
cursor: default;
}
/deep/.el-input-number.is-controls-right:hover .el-input-number__decrease,
/deep/.el-input-number.is-controls-right:hover .el-input-number__increase {
display: none;
}
</style>
\ No newline at end of file
<template>
<div class="container right_content" style="min-width:1200px;">
<el-row type="flex" justify="space-between">
<div class="search">
<el-input v-model="search.search" @change="getList" prefix-icon="el-icon-search"
placeholder="请输入活动名称" style="width: 260px" clearable />
<el-select v-model="search.activityCategoryId" @change="getList" placeholder="全部分类"
class="ml10" clearable>
<el-option v-for="item in categoryList" :key="item.activityCategoryId"
:value="item.activityCategoryId" :label="item.categoryName" />
</el-select>
</div>
<div class="right-btn">
<el-button @click="categoryVisible=true" type="primary"
v-if="$getButtonLimit($buttonCode.activityCategory)"
:limit-code="$buttonCode.activityCategory">分类管理</el-button>
</div>
</el-row>
<div class="mt20">
<el-table :data="tableData" element-loading-text="拼命加载中" v-loading="loading">
<el-table-column v-for="(v, i) in tableHeader" :key="i" v-bind="{...v}"
show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<el-image v-else-if="v.isImage&&scope.row.activityImage"
style="width:40px;height:40px;border-radius:4px;" :src="scope.row.activityImage"
:preview-src-list="[scope.row.activityImage]" />
<span v-else>{{ scope.row[v.prop] || '--' }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="100">
<template slot-scope="{ row }">
<el-button type="text" @click="onEdit(row)"
v-if="$getButtonLimit($buttonCode.activityEdit)"
:limit-code="$buttonCode.activityEdit">
编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
<category-dialog :visible.sync="categoryVisible" @refresh="init"/>
<new-activity :visible.sync="activityVisible" :category-list="categoryList" :item="editItem"
@refresh="getList" />
</div>
</template>
<script>
import requestApi from '@/api/operation';
const { activityGetList, activityCategoryGetList } = requestApi;
import CategoryDialog from './component/CategoryDialog';
import NewActivity from './component/NewActivity';
export default {
components: {
CategoryDialog,
NewActivity
},
data() {
return {
loading: false,
categoryList: [],
categoryFilter: {},
activityList: [],
search: {
search: '',
activityCategoryId: ''
},
tableData: [],
tableHeader: [],
editItem: {},
categoryVisible: false,
activityVisible: false
};
},
created() {
this.init();
},
methods: {
getTableHeader() {
this.tableHeader = [
{ label: '活动分类', prop: 'activityCategoryId', 'min-width': '100', formatter: ({ activityCategoryId = '' }) => ( this.categoryFilter[ activityCategoryId ] ? this.categoryFilter[ activityCategoryId ] : '--') },
{ label: '活动名称', prop: 'activityName', 'min-width': '100' },
{ label: '活动说明', prop: 'activityExplain', 'min-width': '220' },
{ label: '标签文案', prop: 'activityRemark', 'min-width': '100' },
{ label: '排序号', prop: 'activitySort', 'min-width': '100' },
{ label: '活动图片', prop: 'activityImage', 'min-width': '100', isImage: true },
{
label: '添加时间', prop: 'createTime', 'width': '180', formatter: ({ createTime = '' }) => this.$options.filters.formatDate(createTime)
},
];
},
init() {
this.loading = true;
this.getTableHeader();
this.getList();
this.getCategoryList();
this.loading = false;
},
async getList() {
const { search, activityCategoryId } = this.search;
const para = {
search,
activityCategoryId,
orderBy: 'create_time DESC'
};
const res = await activityGetList(para);
this.tableData = res?.result || [];
},
async getCategoryList() {
let { result = [] } = await activityCategoryGetList();
let categoryFilter = {};
result = result.map(el => {
categoryFilter[ el.activityCategoryId ] = el.categoryName;
return el;
});
this.categoryFilter = { ...categoryFilter };
this.categoryList = result || [];
},
onEdit(row) {
this.editItem = { ...this.editItem, ...row };
this.activityVisible = true;
},
onDelete(row) {
console.log(row);
}
}
};
</script>
<style scoped lang="scss"></style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment