Zory 2 hete
szülő
commit
581eebaef0

+ 0 - 14
src/router/systemRouter.js

@@ -1,21 +1,7 @@
 import config from "@/config"
-import tool from "@/utils/tool";
 
 
 var dashboard = "/";
-var userType = tool.data.get("USER_TYPE");
-if (!userType) {
-	dashboard = "/dashboard";
-}
-if (userType == 1) {
-	dashboard = "/manage/dashboard";
-} else if (userType == 2) {
-	dashboard = "/merchant/dashboard";
-} else if (userType == 3) {
-	dashboard = "/service/dashboard";
-} else if (userType == 4) {
-	dashboard = "/factory/dashboard";
-}
 
 //系统路由
 const routes = [

+ 189 - 38
src/views/dashboard/index.vue

@@ -1,44 +1,195 @@
 <template>
-	<div class="router-err">
-		<div class="router-err__icon">
-			<img src="img/404.png" />
-		</div>
-		<div class="router-err__content">
-			<h2>无权限或找不到页面</h2>
-			<p>当前页面无权限访问或者打开了一个不存在的链接,请检查当前账户权限和链接的可访问性。</p>
-			<el-button type="primary" plain round @click="gohome">返回首页</el-button>
-			<el-button type="primary" plain round @click="gologin">重新登录</el-button>
-			<el-button type="primary" round @click="goback">返回上一页</el-button>
-		</div>
-	</div>
+    <div v-if="pageLoading">
+        <el-main>
+            <el-card shadow="never">
+                <el-skeleton :rows="5" animated></el-skeleton>
+            </el-card>
+            <el-card shadow="never" style="margin-top: 15px;">
+                <el-skeleton animated></el-skeleton>
+            </el-card>
+        </el-main>
+    </div>
+    <el-main v-else>
+        <el-card shadow="never" header="实时概况" class="no-boder">
+            <el-row :gutter="10">
+                <el-col :span="6">
+                    <div class="total-panel">
+                        <div class="icon"><el-icon size="22" color="#4799ff"><el-icon-money /></el-icon></div>
+                        <div class="right">
+                            <div class="title">销售额 (元)</div>
+                            <div class="number">0.00</div>
+                        </div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel">
+                        <div class="right">
+                            <div class="title">退款金额</div>
+                            <div class="number">0.00</div>
+                        </div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel">
+                        <div class="icon"><el-icon size="22" color="#4799ff"><el-icon-pie-chart /></el-icon></div>
+                        <div class="right">
+                            <div class="title">商品总数量</div>
+                            <div class="number">0.00</div>
+                        </div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel">
+                        <div class="right">
+                            <div class="title">付款订单总量</div>
+                            <div class="number">0</div>
+                        </div>
+                    </div>
+                </el-col>
+            </el-row>
+        </el-card>
+        <!-- <div class="total-panel-list">
+            <el-row :gutter="20">
+                <el-col :span="6">
+                    <div class="total-panel padding15">
+                        <div class="icon"><el-icon size="22" color="#4799ff"><el-icon-coin /></el-icon></div>
+                        <div class="right">
+                            <div class="title">商品总数量</div>
+                            <div class="number">0.00</div>
+                        </div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel padding15">
+                        <div class="icon"><el-icon size="22" color="#4799ff"><el-icon-coin /></el-icon></div>
+                        <div class="right">
+                            <div class="title">会员总人数</div>
+                            <div class="number">0.00</div>
+                        </div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel padding15">
+                        <div class="icon"><el-icon size="22" color="#4799ff"><el-icon-coin /></el-icon></div>
+                        <div class="right">
+                            <div class="title">付款订单总量</div>
+                            <div class="number">0.00</div>
+                        </div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel padding15">
+                        <div class="icon"><el-icon size="22" color="#4799ff"><el-icon-coin /></el-icon></div>
+                        <div class="right">
+                            <div class="title">消费总人数</div>
+                            <div class="number">0.00</div>
+                        </div>
+                    </div>
+                </el-col>
+            </el-row>
+        </div> -->
+        <div class="total-order-status">
+            <el-row :gutter="20">
+                <el-col :span="6">
+                    <div class="total-panel center no-flex">
+                        <div class="title">待发货订单(笔)</div>
+                        <div class="number">0</div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel center no-flex">
+                        <div class="title">售后单(笔)</div>
+                        <div class="number">0</div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel center no-flex">
+                        <div class="title">待核销订单(笔)</div>
+                        <div class="number">0</div>
+                    </div>
+                </el-col>
+                <el-col :span="6">
+                    <div class="total-panel center no-flex">
+                        <div class="title">待付款订单(笔)</div>
+                        <div class="number">0</div>
+                    </div>
+                </el-col>
+            </el-row>
+        </div>
+        <div class="total-order-status mt10">
+            <scEcharts height="500px" :option="option"></scEcharts>
+        </div>
+    </el-main>
 </template>
 
 <script>
-	export default {
-		methods: {
-			gohome(){
-				location.href="#/"
-			},
-			goback(){
-				this.$router.go(-1);
-			},
-			gologin(){
-				this.$router.push("/login");
-			}
-		}
-	}
+import scEcharts from '@/components/scEcharts';
+export default {
+    components: {
+        scEcharts
+    },
+    data(){
+        return {
+            pageLoading:false,
+            option2: {},
+            option: {
+                title: {
+                    left: 'left', 
+                    text: '订单数据',
+                },
+                grid: {
+                    top: '80px'
+                },
+                tooltip: {
+                    trigger: 'axis'
+                },
+                legend: {
+                    x: 'center',
+                    data: ['订单金额', '订单数']
+                },
+                calculable: true,
+                xAxis: {
+                    type: 'category',
+                    data: ['06-25', '06-26', '06-27', '06-28', '06-29', '06-30', '07-01', '07-02', '07-03', '07-04']
+                },
+                yAxis: [{
+                    type: 'value'
+                }],
+                // yAxis: [{type: 'value', splitLine: {show: true}, gridIndex: 0, axisLabel: {formatter: '{value} 单'}}],
+                series: [{
+                    name: '订单金额',
+                    data: [120, 200, 50, 80, 170, 210, 130,450,244,168],
+                    smooth: true, showBackground: false,
+                    areaStyle: {color: 'rgba(64, 158, 254, 0.6)'},
+                    type: 'line', showSymbol: true, xAxisIndex: 0, yAxisIndex: 0,
+                    emphasis:{focus: 'series'},
+                    label: {position: 'top', formatter: '{c}', show: true},
+                },{
+                    name: '订单数',
+                    data: [20, 210, 10, 180, 110, 100, 130,450,244,168],
+                    smooth: true, showBackground: false,
+                    areaStyle: {color: 'rgba(54, 206, 159, 0.6)'},
+                    type: 'line', showSymbol: true, xAxisIndex: 0, yAxisIndex: 0,
+                    emphasis:{focus: 'series'},
+                    label: {position: 'top', formatter: '{c}', show: true},
+                }]
+            },
+        }
+    }
+}
 </script>
 
-<style scoped>
-	.router-err {display: flex;width: 900px;margin: 50px auto;align-items: center;}
-	.router-err__icon {width: 400px;}
-	.router-err__icon img {width: 100%;}
-	.router-err__content {flex: 1;padding:40px;}
-	.router-err__content h2 {font-size: 26px;}
-	.router-err__content p {font-size: 14px;color: #999;margin: 15px 0 30px 0;line-height: 1.5;}
-
-	@media (max-width: 992px){
-		.router-err {display: block;width: 100%;margin-top: 10px;text-align: center;}
-		.router-err__icon {width: 280px;margin: 0 auto;}
-	}
-</style>
+<style>
+.no-boder{border: 0;}
+.total-panel{display: flex;align-items: center;gap: 20px;background-color: #fff;border-radius: 5px;flex: 1;}
+.total-panel.padding15{padding: 15px;}
+.total-panel.no-flex{flex-direction: column;justify-content: center;gap: 5px;}
+.total-panel.no-flex .title{font-weight: bold;}
+.total-panel.center .right{text-align: center;}
+.total-panel .icon{width: 58px;height: 58px;border-radius: 50%;background-color: #eff6ff;display: flex;align-items: center;justify-content: center;}
+.total-panel .title{font-size: 13px;color: #666;}
+.total-panel .number{font-size: 24px;color: #000;line-height: 38px;}
+.total-panel-list{margin: 20px 0;}
+.total-order-status{background-color: #fff;padding: 20px;margin-top: 20px;}
+.mt10{margin-top: 20px;}
+</style>

+ 1 - 12
src/views/login/components/mobile.vue

@@ -181,22 +181,11 @@ export default {
 				this.$message.warning(menu.message)
 				return false
 			}
-			var userType = userData.data.type;
-			var dashboard = "/dashboard";
-			if (userType == 1) {
-				dashboard = "/manage/dashboard";
-			} else if (userType == 2) {
-				dashboard = "/merchant/dashboard";
-			} else if (userType == 3) {
-				dashboard = "/service/dashboard";
-			} else if (userType == 4) {
-				dashboard = "/factory/dashboard";
-			}
 			this.$emit("success");
 			this.$message.success(user.msg)
 			this.islogin = false;
 			this.$router.replace({
-				path: dashboard
+				path: "/"
 			})
 		}
 	}

+ 1 - 10
src/views/login/components/passwd.vue

@@ -126,19 +126,10 @@ export default {
 				this.$message.warning(menu.message)
 				return false
 			}
-			var userType = userData.data.type;
-			var dashboard = "/dashboard";
-			if (userType == 1) {
-				dashboard = "/manage/dashboard";
-			} else if (userType == 2) {
-				dashboard = "/merchant/dashboard";
-			} else if (userType == 3) {
-				dashboard = "/service/dashboard";
-			}
 			this.$emit("success");
 			this.$message.success(user.msg)
 			this.$router.replace({
-				path: dashboard
+				path: "/"
 			})
 		},
 		onClose() {

+ 201 - 0
src/views/setting/basic/index.vue

@@ -0,0 +1,201 @@
+<template>
+    <el-container>
+        <el-main>
+            <el-card shadow="never" header="系统设置" class="borderNone mt10">
+                <el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleClick">
+                    <el-tab-pane label="基础设置" name="service" v-loading="isLoading">
+                        <el-form :model="sys" :rules="rules" ref="form" label-position="top" @keyup.enter="saveForm">
+                            <el-form-item label="系统名称" prop="title" class="label-item">
+                                <el-input v-model="sys.title" placeholder="请输入" clearable />
+                                <div class="el-form-item-msg"></div>
+                            </el-form-item>
+                            <el-form-item label="联系电话" prop="tel" class="label-item">
+                                <el-input v-model="sys.tel" placeholder="请输入" clearable />
+                                <div class="el-form-item-msg"></div>
+                            </el-form-item>
+                            <el-form-item label="系统Logo" prop="logo">
+                                <sc-upload v-model="sys.logo" title="请上传"></sc-upload>
+                            </el-form-item>
+                            <el-form-item label="备案号" prop="miitbeian" class="label-item">
+                                <el-input v-model="sys.miitbeian" placeholder="请输入" clearable />
+                                <div class="el-form-item-msg">支持Html格式</div>
+                            </el-form-item>
+                            <el-form-item label="版权信息" prop="copyright" class="label-item">
+                                <el-input v-model="sys.copyright" placeholder="请输入" clearable />
+                                <div class="el-form-item-msg"></div>
+                            </el-form-item>
+                            <el-form-item label="隐私协议" prop="privacy" class="label-item">
+                                <sc-editor v-model="sys.privacy" placeholder="请输入" :height="400"></sc-editor>
+                                <div class="el-form-item-msg"></div>
+                            </el-form-item>
+                            <el-form-item label="服务协议" prop="agreements" class="label-item">
+                                <sc-editor v-model="sys.agreements" placeholder="请输入" :height="400"></sc-editor>
+                                <div class="el-form-item-msg"></div>
+                            </el-form-item>
+                        </el-form>
+                    </el-tab-pane>
+                    <el-tab-pane label="小程序设置" name="wechat" v-loading="isLoading">
+                        <el-form :model="sys" :rules="rules2" ref="form" label-position="top" @keyup.enter="saveForm" v-if="activeName=='wechat'">
+                            <el-row :gutter="15">
+                                <el-col :span="this.$store.state.global.ismobile?24:12">
+                                    <fieldset>
+                                        <legend><el-tag>小程序配置</el-tag></legend>
+                                        <el-form-item label="AppId(小程序 APPID)" prop="appid">
+                                            <el-input v-model="sys.appid" clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg">小程序后台获取</div>
+                                        </el-form-item>
+                                        <el-form-item label="AppSecret(小程序密钥)" prop="secret">
+                                            <el-input v-model="sys.secret" show-password clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg">小程序后台获取</div>
+                                        </el-form-item>
+                                        <el-form-item label="分享标题" prop="share_title">
+                                            <el-input v-model="sys.share_title" clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg">小程序分享显示</div>
+                                        </el-form-item>
+                                        <el-form-item label="分享图片" prop="share_img">
+                                            <sc-upload v-model="sys.share_img" title="请上传"></sc-upload>
+                                            <div class="el-form-item-msg">小程序分享显示</div>
+                                        </el-form-item>
+                                    </fieldset>
+                                </el-col>
+                                <el-col :span="this.$store.state.global.ismobile?24:12">
+                                    <fieldset>
+                                        <legend><el-tag>支付配置</el-tag></legend>
+                                        <el-form-item label="MCH_ID(商户账号)" prop="mch_id">
+                                            <el-input v-model="sys.mch_id" clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg">微信商户账号,需要在微信商户平台获取,MCH_ID 与 APPID 匹配</div>
+                                        </el-form-item>
+                                        <el-form-item label="支付密钥" prop="mini_secret">
+                                            <el-input v-model="sys.mini_secret" show-password clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg"></div>
+                                        </el-form-item>
+                                        <el-form-item label="应用公钥" prop="app_public_key">
+                                            <el-input v-model="sys.app_public_key" type="textarea" rows="10" show-password clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg"></div>
+                                        </el-form-item>
+                                        <el-form-item label="平台公钥" prop="public_key">
+                                            <el-input v-model="sys.public_key" type="textarea" rows="10" show-password clearable placeholder="请输入"></el-input>
+                                            <div class="el-form-item-msg"></div>
+                                        </el-form-item>
+                                    </fieldset>
+                                </el-col>
+                            </el-row>
+                        </el-form>
+                    </el-tab-pane>
+                    
+                </el-tabs>
+            </el-card>
+        </el-main>
+        <el-footer>
+            <el-button type="primary" size="large" :loading="isSaveing" @click="saveForm">保存</el-button>
+        </el-footer>
+    </el-container>
+</template>
+<script>
+import { defineAsyncComponent } from 'vue';
+const scEditor = defineAsyncComponent(() => import('@/components/scEditor'));
+export default {
+    components: {
+        scEditor
+    },
+    data(){
+        return {
+            activeName:"service",
+            isSaveing: false,
+            sys: {},
+            smsRegion:[],
+            rules2:{
+                appid: [
+                    { required: true, message: '请输入' }
+                ],
+                secret: [
+                    { required: true, message: '请输入' }
+                ],
+                share_title: [
+                    { required: true, message: '请输入' }
+                ],
+                share_img: [
+                    { required: true, message: '请上传' }
+                ],
+                mch_id: [
+                    { required: true, message: '请输入' }
+                ],
+                mini_secret: [
+                    { required: true, message: '请输入' }
+                ],
+                mini_public_key: [
+                    { required: true, message: '请输入' }
+                ],
+                min_private_key: [
+                    { required: true, message: '请输入' }
+                ],
+            },
+            rules: {
+                title: [
+                    { required: true, message: '请输入' }
+                ],
+                tel: [
+                    { required: true, message: '请输入' }
+                ],
+                logo: [
+                    { required: true, message: '请选择', trigger:"change" }
+                ],
+                miitbeian: [
+                    { required: true, message: '请输入' }
+                ],
+                beian: [
+                    { required: true, message: '请输入' }
+                ],
+                copyright: [
+                    { required: true, message: '请输入' }
+                ]
+            },
+            isLoading:false
+        }
+    },
+    created() {
+        this.getConfig();
+    },
+    methods: {
+        handleClick(event){
+            this.activeName = event;
+            this.getConfig();
+        },
+        saveForm() {
+            this.isSaveing = true;
+            this.$refs.form.validate(async (valid) => {
+                console.log(valid)
+                if (valid) {
+                    var result = await this.$API.setting.configSave.post({type:this.activeName,data: this.sys });
+                    this.isSaveing = false;
+                    if(result.code == 0)
+                    {
+                        this.$message.error(result.msg);
+                        return false;
+                    }
+                    if (this.activeName == "service") {
+                        this.$TOOL.data.set("SERVICE", this.sys)
+                    }
+                    this.$message.success(result.msg);
+                } else {
+                    this.isSaveing = false;
+                    return false;
+                }
+            })
+        },
+        async getConfig() {
+            this.isLoading = true;
+            var result = await this.$API.setting.config.get({ type: this.activeName });
+            this.isLoading = false;
+            if (result.code == 0) {
+                this.$message.error(result.msg);
+                return;
+            }
+            if (this.activeName == "sms") {
+                this.smsRegion = result.data.channel
+            }
+            this.sys = result.data;
+        },
+    }
+}
+</script>

+ 167 - 0
src/views/setting/menu/index.vue

@@ -0,0 +1,167 @@
+<template>
+    <el-container>
+		<el-aside width="300px" v-loading="menuloading">
+			<el-container>
+				<el-header>
+					<el-input placeholder="输入关键字进行过滤" v-model="menuFilterText" clearable></el-input>
+				</el-header>
+				<el-main class="nopadding">
+					<el-tree ref="menu" class="menu" node-key="id" :data="menuList" :props="menuProps" draggable highlight-current :expand-on-click-node="false" check-strictly :filter-node-method="menuFilterNode" @node-click="menuClick" @node-drop="nodeDrop">
+						<template #default="{node, data}">
+							<span class="custom-tree-node">
+								<span class="label">
+									{{ node.label }}
+								</span>
+								<span class="do">
+									<el-button icon="el-icon-plus" size="small" @click.stop="add(node, data)"></el-button>
+								</span>
+							</span>
+						</template>
+					</el-tree>
+				</el-main>
+				<el-footer style="height:51px;">
+					<el-button type="primary" size="small" icon="el-icon-plus" @click="add()"></el-button>
+					<el-button type="danger" size="small" plain icon="el-icon-delete" @click="delMenu"></el-button>
+				</el-footer>
+			</el-container>
+		</el-aside>
+		<el-container>
+			<el-main class="nopadding" style="padding:20px;" ref="main">
+				<save ref="save" @success="getMenu" :type="activeName" :menu="menuList"></save>
+			</el-main>
+		</el-container>
+	</el-container>
+</template>
+
+<script>
+let newMenuIndex = 1;
+import save from './save'
+export default {
+    name: 'settingMenu',
+    components: {
+        save
+    },
+    data() {
+        return {
+            activeName:1,
+            menuloading: false,
+            menuList: [],
+            menuProps: {
+                label: (data)=>{
+                    return data.meta.title
+                }
+            },
+            menuFilterText: ""
+        }
+    },
+    watch: {
+        menuFilterText(val){
+            this.$refs.menu.filter(val);
+        }
+    },
+    mounted() {
+        this.getMenu();
+    },
+    methods: {
+        handleClick(e){
+            this.activeName = e;
+            this.getMenu();
+        },
+        //加载树数据
+        async getMenu(){
+            this.menuloading = true
+            var res = await this.$API.menu.list.get({"form":this.activeName,"type":2});
+            this.menuloading = false
+            if(res.code == 1)
+            {
+                this.menuList = res.data;
+            }
+        },
+        //树点击
+        menuClick(data, node){
+            var pid = node.level==1?undefined:node.parent.data.id;
+            this.$refs.save.setData(data, pid)
+            this.$refs.main.$el.scrollTop = 0
+        },
+        //树过滤
+        menuFilterNode(value, data){
+            if (!value) return true;
+            var targetText = data.meta.title;
+            return targetText.indexOf(value) !== -1;
+        },
+        //树拖拽
+        nodeDrop(draggingNode, dropNode, dropType){
+            this.$refs.save.setData({})
+            this.$message(`拖拽对象:${draggingNode.data.meta.title}, 释放对象:${dropNode.data.meta.title}, 释放对象的位置:${dropType}`)
+        },
+        //增加
+        async add(node, data){
+            var newMenuName = "未命名" + newMenuIndex++;
+            var newMenuData = {
+                pid: data ? data.id : 0,
+                title: newMenuName,
+                from:this.activeName,
+                path: "",
+                component: "",
+                type: "menu"
+            }
+            this.menuloading = true
+            var res = await this.$API.menu.save.post(newMenuData)
+            this.menuloading = false
+            if(res.code !== 1){
+                this.$message.warning(res.msg);
+                return ;
+            }
+            this.getMenu();
+        },
+        //删除菜单
+        async delMenu(){
+            var CheckedNodes = this.$refs.menu.getCheckedNodes()
+            if(CheckedNodes.length == 0){
+                this.$message.warning("请选择需要删除的项")
+                return false;
+            }
+
+            var confirm = await this.$confirm('确认删除已选择的菜单吗?','提示', {
+                type: 'warning',
+                confirmButtonText: '删除',
+                confirmButtonClass: 'el-button--danger'
+            }).catch(() => {})
+            if(confirm != 'confirm'){
+                return false
+            }
+
+            this.menuloading = true
+            var reqData = {
+                ids: CheckedNodes.map(item => item.id)
+            }
+            var res = await this.$API.demo.post.post(reqData)
+            this.menuloading = false
+
+            if(res.code == 200){
+                CheckedNodes.forEach(item => {
+                    var node = this.$refs.menu.getNode(item)
+                    if(node.isCurrent){
+                        this.$refs.save.setData({})
+                    }
+                    this.$refs.menu.remove(item)
+                })
+            }else{
+                this.$message.warning(res.message)
+            }
+        }
+    }
+}
+</script>
+<style scoped>
+	.menu:deep(.el-tree-node__label) {display: flex;flex: 1;height:100%;}
+	.custom-tree-node {display: flex;flex: 1;align-items: center;justify-content: space-between;font-size: 14px;height:100%;padding-right:24px;}
+	.custom-tree-node .label {display: flex;align-items: center;;height: 100%;}
+	.custom-tree-node .label .el-tag {margin-left: 5px;}
+	.custom-tree-node .do {display: none;}
+	.custom-tree-node .do i {margin-left:5px;color: #999;}
+	.custom-tree-node .do i:hover {color: #333;}
+
+	.custom-tree-node:hover .do {display: inline-block;}
+    .tabs{padding: 0 10px;}
+</style>

+ 160 - 0
src/views/setting/menu/save.vue

@@ -0,0 +1,160 @@
+<template>
+	<el-row :gutter="40">
+		<el-col v-if="!form.id">
+			<el-empty description="请选择左侧菜单后操作" :image-size="100"></el-empty>
+		</el-col>
+		<template v-else>
+			<el-col :lg="12">
+				<h2>{{form.meta.title || "新增菜单"}}</h2>
+				<el-form :model="form" :rules="rules" ref="dialogForm" label-width="80px" label-position="left">
+					<el-form-item label="显示名称" prop="form.title">
+						<el-input v-model="form.title" clearable placeholder="菜单显示名字"></el-input>
+					</el-form-item>
+					<el-form-item label="上级菜单" prop="pid">
+						<el-cascader v-model="form.pid" :options="menuOptions" :props="menuProps" :show-all-levels="false" placeholder="顶级菜单" clearable disabled></el-cascader>
+					</el-form-item>
+					<el-form-item label="类型" prop="form.type">
+						<el-radio-group v-model="form.type">
+							<el-radio-button label="menu">菜单</el-radio-button>
+							<el-radio-button label="iframe">Iframe</el-radio-button>
+							<el-radio-button label="link">外链</el-radio-button>
+							<el-radio-button label="button">按钮</el-radio-button>
+						</el-radio-group>
+					</el-form-item>
+					<el-form-item label="类型" prop="form.hide">
+						<el-radio-group v-model="form.hide">
+							<el-radio-button :label="1">显示</el-radio-button>
+							<el-radio-button :label="2">隐藏</el-radio-button>
+						</el-radio-group>
+					</el-form-item>
+					<el-form-item label="别名" prop="name">
+						<el-input v-model="form.name" clearable placeholder="菜单别名"></el-input>
+						<div class="el-form-item-msg">系统唯一且与内置组件名一致,否则导致缓存失效。如类型为Iframe的菜单,别名将代替源地址显示在地址栏</div>
+					</el-form-item>
+					<el-form-item label="菜单图标" prop="meta.icon">
+						<sc-icon-select v-model="form.icon" clearable></sc-icon-select>
+					</el-form-item>
+					<el-form-item label="路由地址" prop="path">
+						<el-input v-model="form.path" clearable placeholder=""></el-input>
+					</el-form-item>
+					<el-form-item label="视图" prop="component">
+						<el-input v-model="form.component" clearable placeholder="">
+							<template #prepend>views/</template>
+						</el-input>
+						<div class="el-form-item-msg">如父节点、链接或Iframe等没有视图的菜单不需要填写</div>
+					</el-form-item>
+					<el-form-item label="排序" prop="sort">
+						<el-input v-model="form.sort" clearable placeholder=""></el-input>
+					</el-form-item>
+					<el-form-item>
+						<el-button type="primary" size="large" @click="save" :loading="loading">保 存</el-button>
+					</el-form-item>
+				</el-form>
+
+			</el-col>
+		</template>
+	</el-row>
+
+</template>
+
+<script>
+	import scIconSelect from '@/components/scIconSelect'
+
+	export default {
+		components: {
+			scIconSelect
+		},
+        emits: ['success'],
+		props: {
+			menu: { type: Object, default: () => {} },
+			type: { type: String, default: "" },
+		},
+		data(){
+			return {
+				form: {
+					id: "",
+					pid: "",
+					name: "",
+					path: "",
+					component: "",
+					redirect: "",
+					apiList: []
+				},
+				menuOptions: [],
+				menuProps: {
+					value: 'id',
+					label: 'title',
+					checkStrictly: true
+				},
+				predefineColors: [
+					'#ff4500',
+					'#ff8c00',
+					'#ffd700',
+					'#67C23A',
+					'#00ced1',
+					'#409EFF',
+					'#c71585'
+				],
+				rules: [],
+				apiListAddTemplate: {
+					code: "",
+					url: ""
+				},
+				loading: false
+			}
+		},
+		watch: {
+			menu: {
+				handler(){
+					this.menuOptions = this.treeToMap(this.menu)
+				},
+				deep: true
+			}
+		},
+		mounted() {
+
+		},
+		methods: {
+			//简单化菜单
+			treeToMap(tree){
+				const map = []
+				tree.forEach(item => {
+					var obj = {
+						id: item.id,
+						parentId: item.parentId,
+						title: item.meta.title,
+						children: item.children&&item.children.length>0 ? this.treeToMap(item.children) : null
+					}
+					map.push(obj)
+				})
+				return map
+			},
+			//保存
+			async save(){
+				this.loading = true
+				var res = await this.$API.menu.save.post(this.form)
+				this.loading = false
+				if(res.code == 1){
+                    this.$emit('success')
+					this.$message.success(res.msg)
+				}else{
+					this.$message.warning(res.msg)
+				}
+			},
+			//表单注入数据
+			setData(data, pid){
+				this.form = data
+				this.form.apiList = data.apiList || []
+				this.form.parentId = pid
+			}
+		}
+	}
+</script>
+
+<style scoped>
+	h2 {font-size: 17px;color: #3c4a54;padding:0 0 30px 0;}
+	.apilist {border-left: 1px solid #eee;}
+
+	[data-theme="dark"] h2 {color: #fff;}
+	[data-theme="dark"] .apilist {border-color: #434343;}
+</style>

+ 6 - 0
src/views/setting/role/index.vue

@@ -0,0 +1,6 @@
+<template>
+
+</template>
+<script>
+
+</script>

+ 312 - 0
src/views/setting/storage/index.vue

@@ -0,0 +1,312 @@
+<template>
+    <el-container>
+        <el-main>
+            <el-card shadow="never" header="存储配置" class="borderNone">
+                <el-form :model="sys" :rules="rules" ref="form" label-position="top">
+                    <div class="tip-container">
+                        <el-alert title="文件将存储在本地服务器,默认保存在 public/upload 目录,文件以 HASH 命名。" show-icon description="文件存储的目录需要有读写权限,有足够的存储空间。特别注意,本地存储暂不支持图片压缩!" type="info" :closable="false" v-if="typeChange == 'public'" />
+                        <el-alert title="文件将上传到 七牛云 存储" description="对象存储需要配置为公开访问的 Bucket 空间" show-icon type="info" :closable="false" v-if="typeChange == 'qiniu'" />
+                        <el-alert title="文件将上传到 阿里云 OSS 存储,需要配置 OSS 公开访问及跨域策略" description="配置跨域访问 CORS 规则,设置:来源 Origin 为 *,允许 Methods 为 POST,允许 Headers 为 *" show-icon type="info" :closable="false" v-if="typeChange == 'oss'" />
+                        <el-alert title="文件将上传到 腾讯云 COS 存储,需要配置 COS 公有读私有写访问权限及跨域策略" description="配置跨域访问 CORS 规则,设置来源 Origin 为 *,允许 Methods 为 POST,允许 Headers 为 *" show-icon type="info" :closable="false" v-if="typeChange == 'cos'" />
+                    </div>
+                    <el-form-item label="默认上传方式:" prop="type" class="label-item">
+                        <el-col :span="11" :xs="24">
+                            <el-radio-group v-model="sys.type" @change="checkType">
+                                <el-radio label="public" border>本地 (不推荐)</el-radio>
+                                <el-radio label="qiniu" border>七牛云</el-radio>
+                                <el-radio label="oss" border>阿里云</el-radio>
+                                <el-radio label="cos" border>腾讯云</el-radio>
+                            </el-radio-group>
+                        </el-col>
+                    </el-form-item>
+                    <el-form-item label="允许类型:" prop="allow_exts" class="label-item">
+                        <el-col :span="11" :xs="24">
+                            <el-input v-model="sys.allow_exts" placeholder="请输入允许类型"></el-input>
+                        </el-col>
+                        <div class="el-form-item-msg">设置系统允许上传文件的后缀,多个以英文逗号隔开如:png,jpg,rar,doc,未设置允许上传的后缀</div>
+                    </el-form-item>
+                    <div v-if="typeChange == 'public'">
+                    </div>
+                    <div v-if="typeChange == 'qiniu'">
+                        <el-form-item label="访问协议:" prop="qiniu_http_protocol" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-radio-group v-model="sys.qiniu_http_protocol">
+                                    <el-radio label="http" border>HTTP</el-radio>
+                                    <el-radio label="https" border>HTTPS</el-radio>
+                                    <el-radio label="auto" border>AUTO</el-radio>
+                                </el-radio-group>
+                            </el-col>
+                            <div class="el-form-item-msg">七牛云存储访问协议,其中 HTTPS 需要配置证书才能使用( AUTO 为相对协议 )</div>
+                        </el-form-item>
+                        <el-form-item label="存储区域:" prop="qiniu_region" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-select v-model="sys.qiniu_region" style="width: 100%;" placeholder="请选择存储区域" size="large">
+                                    <el-option
+                                        v-for="(item, index) in qiniuRegin"
+                                        :key="index"
+                                        :label="item"
+                                        :value="index"
+                                    />
+                                </el-select>
+                            </el-col>
+                            <div class="el-form-item-msg">七牛云存储空间所在区域,需要严格对应储存所在区域才能上传文件</div>
+                        </el-form-item>
+                        <el-form-item label="空间名称:" prop="qiniu_bucket" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.qiniu_bucket" placeholder="请输入空间名称"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">填写七牛云存储空间名称,如:static</div>
+                        </el-form-item>
+                        <el-form-item label="访问域名:" prop="qiniu_http_domain" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.qiniu_http_domain" placeholder="请输入访问域名"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">填写七牛云存储访问域名,如:static.qiniu.tempfood.cn</div>
+                        </el-form-item>
+                        <el-form-item label="访问密钥:" prop="qiniu_access_key" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.qiniu_access_key" placeholder="请输入访问密钥"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">可以在 [ 七牛云 > 个人中心 ] 设置并获取到访问密钥</div>
+                        </el-form-item>
+                        <el-form-item label="安全密钥:" prop="qiniu_secret_key" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.qiniu_secret_key" placeholder="请输入安全密钥"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">可以在 [ 七牛云 > 个人中心 ] 设置并获取到安全密钥</div>
+                        </el-form-item>
+                    </div>
+                    <div v-if="typeChange == 'oss'">
+                        <el-form-item label="访问协议:" prop="oss_http_protocol" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-radio-group v-model="sys.oss_http_protocol">
+                                    <el-radio label="http" size="large">HTTP</el-radio>
+                                    <el-radio label="https" size="large">HTTPS</el-radio>
+                                    <el-radio label="auto" size="large">AUTO</el-radio>
+                                </el-radio-group>
+                            </el-col>
+                            <div class="el-form-item-msg">阿里云OSS存储访问协议,其中 HTTPS 需要配置证书才能使用(AUTO 为相对协议)</div>
+                        </el-form-item>
+                        <el-form-item label="存储区域:" prop="oss_region" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-select v-model="sys.oss_region" style="width: 100%;" placeholder="请选择存储区域" size="large">
+                                    <el-option
+                                        v-for="(item, index) in ossRegin"
+                                        :key="index"
+                                        :label="item"
+                                        :value="index"
+                                    />
+                                </el-select>
+                            </el-col>
+                            <div class="el-form-item-msg">阿里云存储空间所在区域,需要严格对应储存所在区域才能上传文件</div>
+                        </el-form-item>
+                        <el-form-item label="空间名称:" prop="oss_bucket" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.oss_bucket" placeholder="请输入空间名称"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">填写阿里云存储空间名称,如:static</div>
+                        </el-form-item>
+                        <el-form-item label="访问域名:" prop="oss_http_domain" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.oss_http_domain" placeholder="请输入访问域名"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">填写阿里云存储访问域名,如:static.qiniu.tempfood.cn</div>
+                        </el-form-item>
+                        <el-form-item label="访问密钥:" prop="oss_access_key" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.oss_access_key" placeholder="请输入访问密钥"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">可以在 [ 阿里云 > 个人中心 ] 设置并获取到访问密钥</div>
+                        </el-form-item>
+                        <el-form-item label="安全密钥:" prop="oss_secret_key" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.oss_secret_key" placeholder="请输入安全密钥"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">可以在 [ 阿里云 > 个人中心 ] 设置并获取到安全密钥</div>
+                        </el-form-item>
+                    </div>
+                    <div v-if="typeChange == 'cos'">
+                        <el-form-item label="访问协议:" prop="cos_http_protocol" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-radio-group v-model="sys.cos_http_protocol">
+                                    <el-radio label="http" size="large">HTTP</el-radio>
+                                    <el-radio label="https" size="large">HTTPS</el-radio>
+                                    <el-radio label="auto" size="large">AUTO</el-radio>
+                                </el-radio-group>
+                            </el-col>
+                            <div class="el-form-item-msg">阿里云OSS存储访问协议,其中 HTTPS 需要配置证书才能使用(AUTO 为相对协议)</div>
+                        </el-form-item>
+                        <el-form-item label="存储区域:" prop="cos_region" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-select v-model="sys.cos_region" style="width: 100%;" placeholder="请选择存储区域" size="large">
+                                    <el-option
+                                        v-for="(item, index) in cosRegin"
+                                        :key="index"
+                                        :label="item"
+                                        :value="index"
+                                    />
+                                </el-select>
+                            </el-col>
+                            <div class="el-form-item-msg">腾讯云存储空间所在区域,需要严格对应储存所在区域才能上传文件</div>
+                        </el-form-item>
+                        <el-form-item label="空间名称:" prop="cos_bucket" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.cos_bucket" placeholder="请输入空间名称"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">填写腾讯云存储空间名称,如:static</div>
+                        </el-form-item>
+                        <el-form-item label="访问域名:" prop="cos_http_domain" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.cos_http_domain" placeholder="请输入访问域名"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">填写腾讯云存储访问域名,如:static.tempfood.cn</div>
+                        </el-form-item>
+                        <el-form-item label="访问密钥:" prop="cos_access_key" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.cos_access_key" placeholder="请输入访问密钥"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">可以在 [ 腾讯云 > 个人中心 ] 设置并获取到访问密钥</div>
+                        </el-form-item>
+                        <el-form-item label="安全密钥:" prop="cos_secret_key" class="label-item">
+                            <el-col :span="11" :xs="24">
+                                <el-input v-model="sys.cos_secret_key" placeholder="请输入安全密钥"></el-input>
+                            </el-col>
+                            <div class="el-form-item-msg">可以在 [ 腾讯云 > 个人中心 ] 设置并获取到安全密钥</div>
+                        </el-form-item>
+                    </div>
+                </el-form>
+            </el-card>
+        </el-main>
+        <el-footer>
+            <el-button type="primary" size="large" :loading="isSaveing" @click="saveForm">保存</el-button>
+        </el-footer>
+    </el-container>
+</template>
+
+<script>
+
+export default {
+    data() {
+        return {
+            isSaveing: false,
+            sys: {},
+            configList: [],
+            rules: {
+                type: [
+                    { required: true, message: '请选择上传方式', trigger: 'change' }
+                ],
+                allow_exts: [
+                    { required: true, message: '请输入允许类型', trigger: 'blur' }
+                ],
+                qiniu_http_protocol: [
+                    { required: true, message: '请选择访问协议', trigger: 'change' }
+                ],
+                oss_http_protocol: [
+                    { required: true, message: '请选择访问协议', trigger: 'change' }
+                ],
+                cos_http_protocol: [
+                    { required: true, message: '请选择访问协议', trigger: 'change' }
+                ],
+                qiniu_region: [
+                    { required: true, message: '请选择存储区域', trigger: 'change' }
+                ],
+                oss_region: [
+                    { required: true, message: '请选择存储区域', trigger: 'change' }
+                ],
+                cos_region: [
+                    { required: true, message: '请选择存储区域', trigger: 'change' }
+                ],
+                qiniu_bucket: [
+                    { required: true, message: '请输入空间名称', trigger: 'blur' }
+                ],
+                oss_bucket: [
+                    { required: true, message: '请输入空间名称', trigger: 'blur' }
+                ],
+                cos_bucket: [
+                    { required: true, message: '请输入空间名称', trigger: 'blur' }
+                ],
+                qiniu_http_domain: [
+                    { required: true, message: '请输入访问域名', trigger: 'blur' }
+                ],
+                oss_http_domain: [
+                    { required: true, message: '请输入访问域名', trigger: 'blur' }
+                ],
+                cos_http_domain: [
+                    { required: true, message: '请输入访问域名', trigger: 'blur' }
+                ],
+                qiniu_access_key: [
+                    { required: true, message: '请输入访问密钥', trigger: 'blur' }
+                ],
+                oss_access_key: [
+                    { required: true, message: '请输入访问密钥', trigger: 'blur' }
+                ],
+                cos_access_key: [
+                    { required: true, message: '请输入访问密钥', trigger: 'blur' }
+                ],
+                qiniu_secret_key: [
+                    { required: true, message: '请输入安全密钥', trigger: 'blur' }
+                ],
+                oss_secret_key: [
+                    { required: true, message: '请输入安全密钥', trigger: 'blur' }
+                ],
+                cos_secret_key: [
+                    { required: true, message: '请输入安全密钥', trigger: 'blur' }
+                ],
+            },
+            typeChange:"public",
+            ossRegin:[],
+            cosRegin:[],
+            qiniuRegin: [],
+        }
+    },
+    created() {
+        this.getConfig();
+        this.getRegin();
+    },
+    methods: {
+        checkType(val){
+            this.typeChange = val;
+        },
+        saveForm() {
+            this.isSaveing = true;
+            this.$refs.form.validate(async (valid) => {
+                if (valid) {
+                    var result = await this.$API.setting.configSave.post({type:"storage",data: this.sys });
+                    this.isSaveing = false;
+                    if(result.code == 0)
+                    {
+                        this.$message.error(result.msg);
+                        return false;
+                    }
+                    this.$message.success(result.msg);
+                } else {
+                    this.isSaveing = false;
+                    return false;
+                }
+            })
+        },
+        async getConfig() {
+            var result = await this.$API.setting.config.get({ type: "storage" });
+            if (result.code == 0) {
+                this.$message.error(result.msg);
+                return;
+            }
+            this.typeChange = result.data.type;
+            this.sys = result.data;
+        },
+        async getRegin() {
+            var result = await this.$API.setting.regin.get();
+            if (result.code == 0) {
+                this.$message.error(result.msg);
+                return;
+            }
+            this.ossRegin = result.data.oss;
+            this.cosRegin = result.data.cos;
+            this.qiniuRegin = result.data.qiniu;
+        }
+    }
+}
+
+</script>
+<style>
+</style>

+ 6 - 0
src/views/setting/user/index.vue

@@ -0,0 +1,6 @@
+<template>
+
+</template>
+<script>
+
+</script>