Commit 310e95d7 by liuchenxi

Merge branch 'feature/11月迭代'

parents 18ccf74c 9908052c
......@@ -21,8 +21,5 @@
<script src="//web-1251519181.file.myqcloud.com/components/steps.1.0.8.js"></script>
<script src="//web-1251519181.file.myqcloud.com/components/step.1.0.5.js"></script>
<script src="//web-1251519181.file.myqcloud.com/components/user-info.1.0.3.js"></script>
<!-- <script src="//web-1251519181.file.myqcloud.com/components/layout.1.1.32.js"></script> -->
<!-- <script src="//web-1251519181.file.myqcloud.com/components/store-select.1.1.10.js"></script> -->
<!-- <script src="//web-1251519181.file.myqcloud.com/components/goods-rights-selector.1.0.6.js"></script> -->
</body>
</html>
import getFetch from './getFetch.js';
let api1 = {
/** 登录信息 */
getUserInfo: 'gic-authcenter/loginuser',
/** 列表 */
getList: {
url: '/goods-async-schedulers/config/list',
method: 'get'
},
/** 下拉服务器ip列表 */
serverList: {
url: '/goods-async-schedulers/config/server-list',
method: 'get'
let marketingApi = {
/** 获取全部渠道 */
getAllChannel: '/sms/all-channel-list',
// 获取模板管理列表
getTemplateList: {
method: 'post',
url: '/sms/page-sms-setting'
},
/** 开启同步 */
uploadSync: {
url: '/goods-async-schedulers/config/update-sync',
method: 'post'
// 获取模板管理列表详情表格
getTemplateDetailList: {
method: 'post',
url: '/sms/page-template',
},
getDetail: {
url: '/goods-async-schedulers/config/detail',
method: 'post'
// 提审模板
sendExamine: {
method: 'post',
url: '/sms/audit-template',
},
update: {
url: '/goods-async-schedulers/config/update',
method: 'post'
// 获取通道详情
getSmsSetting: {
method: 'post',
url: '/sms/sms-setting-detail',
// useFormData: true
},
create: {
url: '/goods-async-schedulers/config/create',
method: 'post'
// 修改通道
updateSms: {
method: 'post',
url: '/sms/update-sms-setting',
// useFormData: true
},
importGoodsIndex: '/goods-async-schedulers/config/import-goods-index',
refreshGoodsIndex: '/goods-async-schedulers/config/refresh-goods-index',
refreshProductIndex: '/goods-async-schedulers/config/refresh-product-index', // 索引重建并写入数据
productIndexMapping: '/goods-async-schedulers/config/product-index-mapping',
goodsIndexMapping: '/goods-async-schedulers/config/goods-index-mapping'
// 生成appid
getAppid: '/sms/default-appid',
};
let authApi = {
/** 登录信息 */
getUserInfo: '/loginuser'
};
// api = getFetch(api, '/gic-erp-manage');
api1 = getFetch(api1, '');
marketingApi = getFetch(marketingApi, '/marketing-operation');
authApi = getFetch(authApi, '/gic-authcenter');
export default { ...api1 };
export default { ...marketingApi, ...authApi };
......@@ -4,7 +4,7 @@
<template v-for="item in tabData">
<li
v-if="item.isShow == 1"
:class="['tab-left-list-cell p-l-14', {'active-tab': item.uri === activeTab}]"
:class="['tab-left-list-cell p-l-14', {'active-tab': (item.uri === activeTab) && !item.nodeChildren}]"
:key="item.id"
@click="selectTab(item)">
<!-- <i class="my-icon" :class="['iconfont', item.icon, !!item.onlyIconActive ? 'iconActive' : '']"></i> -->
......@@ -58,8 +58,9 @@ export default {
methods: {
selectTab(item) {
console.log(item);
if (this.activeTab === item.uri) return;
if (this.activeTab === item.uri && !item.nodeChildren) return;
if (Array.isArray(item.nodeChildren) && item.nodeChildren.length > 0) {
console.log('11');
return item.collapsFlag = !item.collapsFlag;
} else {
this.$router.push({ path: item.uri });
......@@ -87,7 +88,7 @@ export default {
menu: {
immediate: true,
handler(menu) {
let menuTree = menu.filter(item => item.code === 'goods_operation')[0] || {};
let menuTree = menu.filter(item => item.code === 'marketing_operation')[0] || {};
console.log(menu, menuTree);
this.tabData = (menuTree.nodeChildren || []).map(item => {
this.$set(item, 'collapsFlag', false);
......
<template>
<el-tooltip placement="top">
<template slot="content">
<p class="tip-popover">{{ text }}</p>
</template>
<i class="el-icon-info"></i>
</el-tooltip>
</template>
<script>
export default {
props: {
text: String
}
};
</script>
<style scoped>
i {
margin-left: 5px;
font-size: 13px;
color: #909399;
}
.tip-popover {
max-width: 400px;
color: #ffffff;
}
</style>
......@@ -50,7 +50,7 @@ export default {
top: 0;
right: 0;
left: 0;
// z-index: 20;
z-index: 20;
display: flex;
justify-content: space-between;
align-items: center;
......
......@@ -12,4 +12,4 @@ export const succCode = '0';
// 未登录、登录超时code
export const notAuthCode = '4002';
// 后端请求拦截的标识,用于区分是ajax请求还是页面跳转
export const isRequest = { isControl: true };
\ No newline at end of file
export const isRequest = { isControl: true };
......@@ -9,13 +9,19 @@ export const routes = [
component: Layout,
children: [
{
path: '/messageTemplate',
path: 'messageTemplate',
name: '短信模板管理',
component: _import('message', 'index')
component: _import('message', 'template')
},
{
path: '/messagePass',
path: 'messageTemplateDetail',
name: '短信模板管理',
component: _import('message', 'template-detail')
},
{
path: 'messagePass',
name: '短信通道管理',
component: _import('message', 'pass-manage')
},
]
}
......
......@@ -8,7 +8,7 @@
}
.right_content {
padding: 10px;
padding: 20px;
}
.mr10 {
margin-right: 10px;
......@@ -34,4 +34,91 @@
}
.mb20 {
margin-bottom: 20px;
}
[class^="dm-status--"]::before {
display: inline-block;
width: 6px;
height: 6px;
margin-right: 7px;
vertical-align: middle;
content: ' ';
background-color: #D9D9D9;
border-radius: 50%;
}
/* 已发送/成功/已完成… */
.dm-status--success::before {
background-color: #52c41a;
}
/* 失败/异常… */
.dm-status--error::before {
background-color: #F5222D;
}
/* 默认 */
.dm-status--info::before {
background-color: #D9D9D9;
}
/* 告警… */
.dm-status--warning::before {
background-color: #FAAD14;
}
/* 进行中 无闪动 */
.dm-status--primary::before {
background-color: #1890FF;
}
/* 进行中 闪动效果 */
.dm-status--primary--flash {
position: relative;
display: inline-block;
vertical-align: middle;
border-radius: 50%;
background: #1890FF;
width: 6px;
height: 6px;
margin-right: 7px;
}
.dm-status--primary--flash::before {
content: ' ';
display: inline-block;
position: absolute;
left: -1px;
top: -1px;
width: 6px;
height: 6px;
background:none;
vertical-align: middle;
border-radius: 50%;
border: 1px solid #1890FF;
-webkit-animation: antStatusProcessing 1.2s ease-in-out infinite;
animation: antStatusProcessing 1.2s ease-in-out infinite;
}
@-webkit-keyframes antStatusProcessing {
0% {
-webkit-transform: scale(.8);
transform: scale(.8);
opacity: 0.5
}
to {
-webkit-transform: scale(2.4);
transform: scale(2.4);
opacity: 0.5
}
}
@keyframes antStatusProcessing {
0% {
-webkit-transform: scale(.8);
transform: scale(.8);
opacity: 1
}
to {
-webkit-transform: scale(1.4);
transform: scale(1.4);
opacity: 0.5
}
}
\ No newline at end of file
import Vue from 'vue';
import Clipboard from 'clipboard';
function clipboardSuccess() {
Vue.prototype.$message({
message: 'Copy successfully',
type: 'success',
duration: 1500
});
}
function clipboardError() {
Vue.prototype.$message({
message: 'Copy failed',
type: 'error'
});
}
export default function handleClipboard(text, event) {
const clipboard = new Clipboard(event.target, {
text: () => text
});
clipboard.on('success', () => {
clipboardSuccess();
clipboard.off('error');
clipboard.off('success');
clipboard.destroy();
});
clipboard.on('error', () => {
clipboardError();
clipboard.off('error');
clipboard.off('success');
clipboard.destroy();
});
clipboard.onClick(event);
}
<template>
<div class="content">
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-form-item label="短信签名ID" prop="smsSignId">
<el-input v-model="form.smsSignId" class="w180" :disabled="disabledSmsSignId" />
<span class="tip">保存后不可编辑,请谨慎填写;如需变更,请提交数据工单处理</span>
</el-form-item>
<el-form-item label="短信签名" prop="smsSignText">
<el-input v-model="form.smsSignText" class="w180" :disabled="disabledSignText" />
<span class="tip">保存后不可编辑,请谨慎填写;如需变更,请提交数据工单处理</span>
</el-form-item>
<el-form-item label="签名应用通道" prop="channelSignList">
<el-checkbox-group v-model="form.channelSignList">
<el-checkbox v-for="v in channelList" :key="v.channelId" :label="v.channelId" :disabled="disabledCheckBox.includes(v.channelId)">{{ v.channelName }}</el-checkbox>
<span span class="tip">请完善短信通道的短信签名后再勾选</span>
</el-checkbox-group>
</el-form-item>
<el-form-item label="短信模板数量" prop="maxTemplateCount">
<el-input-number v-model="form.maxTemplateCount" controls-position="right" :min="form.useTemplateCount" class="w90" />
<span class="tip">该商户最多可创建的短信模板数量</span>
</el-form-item>
<el-form-item label="短信发送优先级" prop="sendSmsType">
<el-radio-group v-model="form.sendSmsType">
<p class="send_sms">
<el-radio :label="0">多通道</el-radio>
<span class="channels">{{ channelSortRes }}</span>
<span class="tip">商户选择多通道后,系统会优先使用高优先级的通道进行发送,若短信提交失败,则换用低优先级的通…</span>
<el-button type="text" style="margin-left:4px;font-size: 12px" @click="dialogVisible = true">查看更多</el-button>
</p>
<el-radio :label="1">
指定通道
<el-radio-group v-model="form.appointChannel" class="ml20" v-if="form.sendSmsType" style="vertical-align: top">
<el-radio v-for="item in channelList" :key="item.channelId" :label="item.channelId">{{ item.channelName }}</el-radio>
</el-radio-group>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="短信AppID" prop="smsAppid">
<span>{{ form.smsAppid }}</span>
<span class="tip">短信AppID由系统自动匹配</span>
</el-form-item>
<el-form-item label="">
<el-button type="primary" style="margin-top: 30px" @click="submit">保存</el-button>
</el-form-item>
</el-form>
<more-tip-dialog title="提示详情" :type="form.smsSignId" :visible.sync="dialogVisible"/>
</div>
</template>
<script>
import requestApi from '@/api/operation';
import MoreTipDialog from './more-tip-dialog.vue';
const { getAllChannel, getSmsSetting, updateSms, getAppid } = requestApi;
export default {
components: {
MoreTipDialog
},
data() {
const sendSmsTypeValidate = (_, val, cb) => {
if (val === 1 && !this.form.appointChannel) {
cb(new Error('请选择指定通道'));
}
cb();
};
return {
isEcho: false,
channelList: [],
disabledCheckBox: [],
disabledSignText: false,
disabledSmsSignId: false,
form: {
enterpriseSmsSettingId: '',
enterpriseId: '',
smsSignId: '',
smsSignText: '',
channelSignList: [],
maxTemplateCount: 0,
useTemplateCount: 0, // 输入限制模板数量
sendSmsType: 0,
appointChannel: null, // 指定通道时的类型
smsAppid: '',
channelSort: '', // 短信通道排序字符串
channelSortRes: '' // 短信排序后的结果
},
rules: {
smsSignId: { required: true, message: '请输入签名Id', trigger: 'blur' },
smsSignText: { required: true, message: '请输入短信签名', trigger: 'blur' },
channelSignList: { required: true, message: '请选择签名应用通道', trigger: 'blur' },
maxTemplateCount: { required: true, message: '请输短信模板数量', trigger: 'blur' },
sendSmsType: { validator: sendSmsTypeValidate, trigger: 'change' }
},
dialogVisible: false
};
},
created() {
this.getChannelList();
},
methods: {
async getDetail() {
let result = await getSmsSetting({ enterpriseId: this.$route.query.id });
result = result.result || {};
for (let key in result) {
if (this.form.hasOwnProperty(key)) {
this.form[key] = result[key] || this.form[key];
if (key == 'channelSignList' && typeof this.form[key] == 'string') {
this.form[key] = this.form[key].split(',').map((el) => +el);
this.disabledCheckBox = this.form[key];
}
if (key == 'sendSmsType' && this.form[key] != 0) {
this.form['appointChannel'] = this.form[key];
this.form[key] = 1;
this.$emit('getTableChannelType', this.form['appointChannel']); // 触发table组件修改通道类型
} else if (key == 'sendSmsType' && this.form[key] == 0) {
this.$emit('getTableChannelType', 0); // 触发table组件修改通道类型
}
if (key == 'smsAppid' && !this.form[key]) {
const { result } = await getAppid();
this.form[key] = result;
}
if (key == 'channelSort' && this.form[key]) {
this.channelSortRes = this.channelSort(this.form[key]);
}
if(key == 'smsSignId' && this.form[key]) {
this.disabledSmsSignId = true;
} else if(key == 'smsSignId' && !this.form[key]) {
this.disabledSmsSignId = false;
}
if(key == 'smsSignText' && this.form[key]) {
this.disabledSignText = true;
} else if(key == 'smsSignText' && !this.form[key]){
this.disabledSignText = false;
}
}
}
},
async getChannelList() {
const { result } = await getAllChannel();
this.channelList = result || [];
this.getDetail();
},
// 表单提交
submit() {
this.$refs.form.validate(async val => {
if (val) {
const para = Object.assign({}, this.form);
if (para.sendSmsType == 1) para.sendSmsType = para.appointChannel;
para.channelSignList = para.channelSignList.toString();
delete para.appointChannel;
delete para.channelSort;
delete para.channelSortRes;
delete para.useTemplateCount;
delete
await updateSms(para);
this.$message.success('保存成功');
this.getDetail();
}
});
},
channelSort(sortStr) {
const sort = sortStr.split(',');
let result = sort.map((el) => {
const res = this.channelList.find((els) => els.channelId == el);
return res ? res.channelName : null;
}).filter(el => !!el);
return result.join(' > ');
},
}
};
</script>
<style scoped lang="scss">
.w180 {
width: 180px;
}
.w90 {
width: 90px;
}
.content {
margin-top: 26px;
.send_sms {
height: 32px;
margin-bottom: 28px;
padding-top: 5px;
box-sizing: border-box;
}
.channels {
font-size: 14px;
color: #606266;
line-height: 20px;
}
}
.tips, .tip {
margin-left: 14px;
font-size: 12px;
color: #909399;
line-height: 17px;
&.tip {
margin-left: 18px;
}
}
</style>
<template>
<el-dialog :title="title" :visible.sync="visible" width="600px" append-to-body>
<template v-if="!type">
<p>1. 商户选择多通道后,系统会优先使用高优先级的通道进行发送,若短信提交失败,则换<i class="w16" />用低优先级的通道,直到所有通道都尝试过。(应用场景:短信营销,智能营销,观云<i class="w16" />台,会员小程序验证码)</p>
<p class="pb20">2. 商户选择指定通道后,只会通过该通道发送。</p>
</template>
<template v-else>
<p>1. 商户选择多通道后,系统会优先使用高优先级的通道进行发送,若短信提交失败,则换用低优先级的通道,直到所有通道都尝试过。(应用场景:短信营销,智能营销,观云台,会员小程序验证码)</p>
<p class="mt10 mb10">2. 商户选择指定通道后,只会通过该通道发送。</p>
<p>3. 修改短信发送优先级注意事项: </p>
<p><i class="w16" />(1) 指定通道切换多通道,可随意切换,无影响,切换前最好进行全量提审操作;</p>
<p><i class="w16" />(2) 多通道切换指定通道/单通道切换单通道,必须进行全量审核(全量提审:把gic后台<i class="w32" />已通过的短信模板提交给其他可用短信通道审核)</p>
<p class="pb20"><i class="w32" />全量提审后,原先已通过审核的模板在要切换的指定通道不通过时,无法进行切换,<i class="w32" />需要人工介入处理后,才可切换指定通道。</p>
</template>
</el-dialog>
</template>
<script>
export default {
props: {
title: String,
type: String,
visible: Boolean
},
watch: {
visible(val) {
if(!val) this.$emit('update:visible', val);
}
}
};
</script>
<style scoped lang="scss">
.pb20 {
padding-bottom: 20px;
}
p {
line-height: 24px;
color: #303133;
}
.indenet {
text-indent: 16px;
}
i {
display: inline-block;
&.w16 {
width: 16px;
}
&.w32 {
width: 32px;
}
}
</style>
<template>
<div class="container right_content">
<div class="mt20">
<el-table :data="tableData.data" element-loading-text="拼命加载中">
<el-table-column v-for="(v, i) in tableData.tableHeader" :key="i" :prop="v.prop" :min-width="v.minWidth" :label="v.label" :formatter="v.formatter" :fixed="v.fixed" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<span v-else>{{ scope.row[v.prop] || '--' }}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import requestApi from '@/api/operation';
const { getAllChannel } = requestApi;
export default {
data() {
return {
tableData: {
data: [],
tableHeader: []
}
};
},
created() {
this.getTableHeader();
this.getTableData();
},
methods: {
// table methods
getTableHeader() {
this.tableData.tableHeader = [
{ label: '通道名称', prop: 'channelName' },
{
label: '发送优先级',
prop: '',
formatter(row) {
return `P${row.index + 1}`;
}
}
];
},
async getTableData() {
const result = await getAllChannel();
const arr = result.result || [];
this.tableData.data = arr.map((el, index) => {
el.index = index;
return el;
});
}
}
};
</script>
<style scoped lang="scss"></style>
<template>
<div class="container right_content">
<div class="top">
<div class="title">
<h2>基本信息</h2>
</div>
<detail-form @getTableChannelType="(val) => tableChannelType = val" />
</div>
<div class="bottom">
<div class="title mb20">
<h2>短信模板列表</h2>
</div>
<detail-table :table-channel-type="tableChannelType" />
</div>
</div>
</template>
<script>
import detailForm from './component/detail-form.vue';
import detailTable from './component/detail-table.vue';
export default {
components: {
detailForm,
detailTable
},
data() {
return {
tableChannelType: null
};
}
};
</script>
<style scoped lang="scss">
.top,
.bottom {
&.bottom {
margin-top: 30px;
}
.title {
background: #f7f8fa;
h2 {
padding-left: 22px;
font-size: 14px;
font-weight: 600;
color: #303133;
height: 40px;
line-height: 40px;
position: relative;
&::before {
content: '';
width: 3px;
height: 14px;
background: #1890ff;
position: absolute;
left: 11px;
top: 13px;
}
}
}
}
</style>
<template>
<div class="container right_content">
<div class="search">
<el-input v-model="searchText" prefix-icon="el-icon-search" placeholder="请输入品牌名称/公司名称" style="width: 234px" />
<el-select v-model="mode" placeholder="选择发送通道" class="ml10">
<el-option v-for="(item, index) in passList" :key="index" :value="item.value">{{ item.label }}</el-option>
<el-input v-model="search.searchText" @change="searchTypeChange" prefix-icon="el-icon-search" placeholder="请输入品牌名称/公司名称" style="width: 234px" />
<el-select v-model="search.channelId" @change="searchTypeChange" placeholder="选择发送通道" class="ml10">
<el-option v-for="item in channelList" :key="item.channelId" :value="item.channelId" :label="item.channelName">{{ item.channelName }}</el-option>
</el-select>
</div>
<div class="mt20">
<el-table :data="tableData.data" element-loading-text="拼命加载中">
<el-table :data="tableData.data" element-loading-text="拼命加载中" v-loading="loading">
<el-table-column v-for="(v, i) in tableData.tableHeader" :key="i" :prop="v.prop" :min-width="v.minWidth" :label="v.label" :formatter="v.formatter" :fixed="v.fixed" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="v.formatter" v-html="v.formatter(scope.row)"></span>
<span v-else>{{ scope.row[v.prop] || '--' }}</span>
</template>
</el-table-column>
<!-- <el-table-column label="操作">
<el-table-column label="操作">
<template slot-scope="{ row }">
<el-button type="text" @click="toDetail(row)">详情</el-button>
</template>
</el-table-column> -->
</el-table>
<dm-pagination background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="tableData.currentPage" :page-sizes="tableData.pageSizeList" :page-size="tableData.pageSize" layout="total, sizes, prev, pager, next" :total="tableData.total" hide-on-single-page />
</el-table-column>
</el-table>
<el-pagination background class="dm-pagination" @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="tableData.currentPage" :page-sizes="tableData.pageSizeList" :page-size="tableData.pageSize" layout="total, sizes, prev, pager, next" :total="tableData.total" hide-on-single-page />
</div>
</div>
</template>
<script>
import requestApi from '@/api/operation';
const { getAllChannel, getTemplateList } = requestApi;
export default {
data() {
return {
passList: [
{
label: '多通道',
value: 1
},
{
label: '腾讯云',
value: 2
},
{
label: '大汉三通',
value: 3
}
],
loading: false,
channelList: [],
search: {
searchText: '',
mode: 0
channelId: ''
},
tableData: {
data: [],
......@@ -59,29 +49,56 @@ export default {
},
created() {
this.getTableHeader();
this.getChannelList();
this.getTableData();
},
methods: {
// table methods
getTableHeader() {
this.tableData.tableHeader = [
{ label: '手机号', prop: 'phoneNumber' },
{ label: '黑名单原因', prop: 'reason' },
{ label: '操作人', prop: 'creatorName' }
{ label: '品牌名称', prop: 'brandName' },
{ label: '公司名称', prop: 'enterpriseName' },
{ label: '短信签名', prop: 'smsSignText' },
{
label: '发送通道',
prop: 'sendSmsType',
formatter(row) {
let text = '';
switch (row.sendSmsType) {
case 0:
text = '多通道';
break;
case 1:
text = '腾讯云';
break;
case 3:
text = '大汉三通';
break;
default:
text = '--';
break;
}
return text;
}
},
{ label: '短信模板总数', prop: 'maxTemplateCount' },
{ label: '已用短信模板数', prop: 'useTemplateCount' }
];
},
async getTableData() {
// const { currentPage, pageSize } = this.tableData;
// const { phone, time } = this.search;
// const para = {
// currentPage,
// pageSize,
// phoneNumber: phone,
// startTime: new Date(time[0]).getTime() || '',
// endTime: new Date(time[1]).getTime() || ''
// };
// const result = await getBlackList(para);
// this.tableData.data = result.result.result || [];
// this.tableData.total = result.result.totalCount || 0;
this.loading = true;
const { currentPage, pageSize } = this.tableData;
const { searchText, channelId } = this.search;
const para = {
pageNum: currentPage,
pageSize,
channel: channelId,
search: searchText
};
const result = await getTemplateList(para);
this.loading = false;
this.tableData.data = result.result.result || [];
this.tableData.total = result.result.totalCount || 0;
},
handleSizeChange(val) {
this.tableData.pageSize = val;
......@@ -93,8 +110,17 @@ export default {
this.getTableData();
},
toDetail(row) {
console.log(row);
}
this.$router.push(`/messageTemplateDetail?id=${row.enterpriseId}`);
},
// others
async getChannelList() {
const { result } = await getAllChannel();
this.channelList = result.concat({ channelId: 0, channelName: '多通道' }) || [];
},
searchTypeChange() {
this.tableData.currentPage = 1;
this.getTableData();
},
}
};
</script>
......
......@@ -2,12 +2,12 @@ const path = require("path");
module.exports = {
productionSourceMap: process.env.NODE_ENV !== 'production',
publicPath: process.env.NODE_ENV === 'production' ? '/goods-operation' : '/',
publicPath: process.env.NODE_ENV === 'production' ? '/gic-marketing-operation' : '/',
devServer: {
overlay: {
warnings: true,
errors: true
},
}
},
configureWebpack: config => {
config.externals = {
......
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