lz vor 3 Jahren
Ursprung
Commit
eba1f50c85

+ 27 - 0
src/components/UploadMultiple/index.vue

@@ -0,0 +1,27 @@
+<template>
+	<el-dialog title="上传文件" :visible.sync="show" width="500px" append-to-body>
+		<div class="upload">
+			<input type="file" name="file" ref="file" multiple="multiple" @change="getFile" style="display: none" :accept="accept" />
+		</div>
+		<div slot="footer" class="dialog-footer">
+			<el-button type="primary" @click="sub">确 定</el-button>
+			<el-button @click="cancel">取 消</el-button>
+		</div>
+	</el-dialog>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				show: false
+			}
+		},
+		methods: {
+			sub() {},
+			cancel() {}
+		}
+	}
+</script>
+
+<style scoped lang="scss"></style>

+ 64 - 11
src/components/UploadOne/index.vue

@@ -5,8 +5,10 @@
 				<i class="el-icon-upload"></i>
 				<span class="upload-text">点击选择文件</span>
 				<span>
-					<small>允许上传格式:jpg/png/gif</small><br />
-					<small>文件大小不超过{{ size }}M</small><br />
+					<small v-if="type == 'img'">允许上传格式:jpg/png/gif</small><br />
+					<small v-if="type == 'doc'">允许上传格式:excel/word/pdf/ppt/txt</small><br />
+					<small v-if="type == 'video'">允许上传格式:mp4/mpeg/ogg</small><br />
+					<small>文件大小不超过:{{ size }}M</small><br />
 				</span>
 			</div>
 			<div class="upload-show" v-else>
@@ -14,8 +16,14 @@
 					<div class="mask"></div>
 					<el-progress type="circle" :percentage="$store.state.oss.progress"></el-progress>
 				</div>
-
-				<img :src="preview" alt="" />
+				<img :src="preview" alt="" v-if="type == 'img'" />
+				<div v-if="type == 'doc'" class="doc-box">
+					<i class="el-icon-document"></i>
+					<span>{{ preview }}</span>
+				</div>
+				<div v-if="type == 'video'" class="video">
+					<Video :src="preview" />
+				</div>
 			</div>
 		</div>
 		<input type="file" name="file" ref="file" @change="getFile" style="display: none" :accept="accept" />
@@ -24,22 +32,29 @@
 
 <script>
 	import upload from '@/utils/oss.js'
+	import Video from '@/components/Video/index.vue'
 	export default {
 		props: {
+			//文件类型 img图片 doc文件 video视频
+			type: {
+				type: String,
+				default: 'img'
+			},
 			//限制文件大小
 			size: {
-				type: Number,
-				default: 3
+				type: String,
+				default: '10'
 			}
 		},
+		components: {
+			Video
+		},
 		data() {
 			return {
 				//文件对象
 				fileObj: null,
-				//预览地址
+				//文件名称
 				preview: null,
-				//文件类型
-				accept: 'image/jpg,image/jpeg,image/png,image/gif',
 				//显示进度条
 				showProgress: false
 			}
@@ -50,6 +65,15 @@
 					return true
 				}
 				return false
+			},
+			accept() {
+				if (this.type == 'img') {
+					return 'image/jpg,image/jpeg,image/png,image/gif'
+				} else if (this.type == 'doc') {
+					return 'application/msexcel,application/msword,application/pdf,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-powerpoint,text/plain'
+				} else if (this.type == 'video') {
+					return 'audio/mp4,video/mp4,video/mpeg,application/ogg,audio/ogg,'
+				}
 			}
 		},
 		methods: {
@@ -72,9 +96,19 @@
 					this.$msg({ type: 'error', message: `文件不能大于${this.size}M` })
 					return
 				}
-				this.preview = URL.createObjectURL(this.fileObj)
+				switch (this.type) {
+					case 'img':
+						this.preview = URL.createObjectURL(this.fileObj)
+						break
+					case 'doc':
+						this.preview = this.fileObj.name
+						break
+					case 'video':
+						this.preview = URL.createObjectURL(this.fileObj)
+						break
+				}
 			},
-			//上传图片到oss并返回图片路径
+			//上传图片到oss并返回路径
 			uploadPut() {
 				if (!this.fileObj) return
 				this.showProgress = true
@@ -118,6 +152,9 @@
 			padding: 10px;
 			box-sizing: border-box;
 			position: relative;
+			display: flex;
+			justify-content: center;
+			align-items: center;
 			.progress {
 				width: 100%;
 				height: 100%;
@@ -141,6 +178,22 @@
 				width: 100%;
 				height: 100%;
 			}
+			.doc-box {
+				width: 60%;
+				height: 100%;
+				display: flex;
+				justify-content: center;
+				flex-direction: column;
+				align-items: center;
+				i {
+					font-size: 100px;
+					color: #c0c4cc;
+				}
+			}
+			.video {
+				width: 100%;
+				height: 100%;
+			}
 		}
 	}
 </style>

+ 52 - 0
src/components/Video/index.vue

@@ -0,0 +1,52 @@
+<template>
+	<video class="czo-video" controls :autoplay="autoplay" preload="none" :style="{ width, height }" :poster="poster">
+		<source :src="src" type="video/mp4" />
+		<source :src="src" type="video/mpeg" />
+		<source :src="src" type="video/ogg" />
+	</video>
+</template>
+
+<script>
+	export default {
+		props: {
+			//封面地址
+			poster: String,
+			//资源地址
+			src: String,
+			//宽度
+			width: {
+				type: String,
+				default: '100%'
+			},
+			//高度
+			height: {
+				type: String,
+				default: '100%'
+			},
+			//是否自动播放
+			autoplay: {
+				type: Boolean,
+				default: true
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.czo-video {
+		display: block;
+		vertical-align: top;
+		-webkit-box-sizing: border-box;
+		box-sizing: border-box;
+		color: #fff;
+		background-color: #000;
+		position: relative;
+		padding: 0;
+		font-size: 10px;
+		line-height: 1;
+		font-weight: normal;
+		font-style: normal;
+		font-family: Arial, Helvetica, sans-serif;
+		word-break: initial;
+	}
+</style>

+ 7 - 24
src/utils/oss.js

@@ -38,30 +38,13 @@ const CooOss = function (file) {
 }
 
 // 下载
-export const download = function (key) {
-	console.log(key)
-	let url = client.signatureUrl(key)
-	let Img = new Image(),
-		dataURL = ''
-	let fileName = key.substring(key.indexOf('_') + 1)
-	Img.src = url
-	Img.setAttribute('crossOrigin', 'Anonymous')
-	Img.onload = function () {
-		let canvas = document.createElement('canvas'),
-			width = Img.width,
-			height = Img.height
-		canvas.width = width
-		canvas.height = height
-		canvas.getContext('2d').drawImage(Img, 0, 0, width, height)
-		dataURL = canvas.toDataURL('image/png')
-		let eleLink = document.createElement('a')
-		eleLink.download = fileName
-		eleLink.style.display = 'none'
-		eleLink.href = dataURL
-		document.body.appendChild(eleLink)
-		eleLink.click()
-		document.body.removeChild(eleLink)
-	}
+export const download = function (key, filename = '') {
+	const url = client.signatureUrl(key, {
+		response: {
+			'content-disposition': `attachment; filename=${encodeURIComponent(filename)};expires: 7200`
+		}
+	})
+	window.location.href = url
 }
 /**
  *

+ 2 - 1
src/utils/request.js

@@ -1,5 +1,5 @@
 import axios from 'axios'
-import { Notification, MessageBox } from 'element-ui'
+import { Notification, MessageBox, Loading } from 'element-ui'
 import store from '@/store'
 import { getToken } from '@/utils/auth'
 import errorCode from '@/utils/errorCode'
@@ -103,6 +103,7 @@ instance.interceptors.response.use(
 		notif(message)
 	}
 )
+
 function notif(msg) {
 	Notification({
 		message: msg,

+ 6 - 9
src/views/index.vue

@@ -1,13 +1,13 @@
 <template>
 	<div>
-		<UploadOne ref="upload" />
+		<UploadOne ref="upload" type="video" size="50" />
 		<button @click="test">上传</button>
-		<img :src="src" alt="暂无图片" width="100" height="100" />
 	</div>
 </template>
 
 <script>
 	import UploadOne from '@/components/UploadOne/index.vue'
+	import Video from '@/components/Video/index.vue'
 	export default {
 		name: 'Index',
 		data() {
@@ -16,14 +16,11 @@
 			}
 		},
 		components: {
-			UploadOne
+			UploadOne,
+			Video
 		},
-		methods: {
-			async test() {
-				const res = await this.$refs.upload.uploadPut()
-				this.src = this.$store.state.oss.ossBaseUrl + res
-			}
-		}
+		mounted() {},
+		methods: { test() {} }
 	}
 </script>
 

+ 369 - 445
src/views/system/menu/index.vue

@@ -1,454 +1,378 @@
 <template>
-  <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
-      <el-form-item label="菜单名称" prop="menuName">
-        <el-input
-          v-model="queryParams.menuName"
-          placeholder="请输入菜单名称"
-          clearable
-          size="small"
-          @keyup.enter.native="handleQuery"
-        />
-      </el-form-item>
-      <el-form-item label="状态" prop="status">
-        <el-select v-model="queryParams.status" placeholder="菜单状态" clearable size="small">
-          <el-option
-            v-for="dict in dict.type.sys_normal_disable"
-            :key="dict.value"
-            :label="dict.label"
-            :value="dict.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
-        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
+	<div class="app-container">
+		<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+			<el-form-item label="菜单名称" prop="menuName">
+				<el-input v-model="queryParams.menuName" placeholder="请输入菜单名称" clearable size="small" @keyup.enter.native="handleQuery" />
+			</el-form-item>
+			<el-form-item label="状态" prop="status">
+				<el-select v-model="queryParams.status" placeholder="菜单状态" clearable size="small">
+					<el-option v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+				</el-select>
+			</el-form-item>
+			<el-form-item>
+				<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+				<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+			</el-form-item>
+		</el-form>
 
-    <el-row :gutter="10" class="mb8">
-      <el-col :span="1.5">
-        <el-button
-          type="primary"
-          plain
-          icon="el-icon-plus"
-          size="mini"
-          @click="handleAdd"
-          v-hasPermi="['system:menu:add']"
-        >新增</el-button>
-      </el-col>
-      <el-col :span="1.5">
-        <el-button
-          type="info"
-          plain
-          icon="el-icon-sort"
-          size="mini"
-          @click="toggleExpandAll"
-        >展开/折叠</el-button>
-      </el-col>
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
+		<el-row :gutter="10" class="mb8">
+			<el-col :span="1.5">
+				<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['system:menu:add']">新增</el-button>
+			</el-col>
+			<el-col :span="1.5">
+				<el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">展开/折叠</el-button>
+			</el-col>
+			<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+		</el-row>
 
-    <el-table
-      v-if="refreshTable"
-      v-loading="loading"
-      :data="menuList"
-      row-key="menuId"
-      :default-expand-all="isExpandAll"
-      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
-    >
-      <el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
-      <el-table-column prop="icon" label="图标" align="center" width="100">
-        <template slot-scope="scope">
-          <svg-icon :icon-class="scope.row.icon" />
-        </template>
-      </el-table-column>
-      <el-table-column prop="orderNum" label="排序" width="60"></el-table-column>
-      <el-table-column prop="perms" label="权限标识" :show-overflow-tooltip="true"></el-table-column>
-      <el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
-      <el-table-column prop="status" label="状态" width="80">
-        <template slot-scope="scope">
-          <dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status"/>
-        </template>
-      </el-table-column>
-      <el-table-column label="创建时间" align="center" prop="createTime">
-        <template slot-scope="scope">
-          <span>{{ parseTime(scope.row.createTime) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-        <template slot-scope="scope">
-          <el-button 
-            size="mini"
-            type="text"
-            icon="el-icon-edit"
-            @click="handleUpdate(scope.row)"
-            v-hasPermi="['system:menu:edit']"
-          >修改</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-plus"
-            @click="handleAdd(scope.row)"
-            v-hasPermi="['system:menu:add']"
-          >新增</el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-delete"
-            @click="handleDelete(scope.row)"
-            v-hasPermi="['system:menu:remove']"
-          >删除</el-button>
-        </template>
-      </el-table-column>
-    </el-table>
+		<el-table v-if="refreshTable" v-loading="loading" :data="menuList" row-key="menuId" :default-expand-all="isExpandAll" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }">
+			<el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
+			<el-table-column prop="icon" label="图标" align="center" width="100">
+				<template slot-scope="scope">
+					<svg-icon :icon-class="scope.row.icon" />
+				</template>
+			</el-table-column>
+			<el-table-column prop="orderNum" label="排序" width="60"></el-table-column>
+			<el-table-column prop="perms" label="权限标识" :show-overflow-tooltip="true"></el-table-column>
+			<el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
+			<el-table-column prop="status" label="状态" width="80">
+				<template slot-scope="scope">
+					<dict-tag :options="dict.type.sys_normal_disable" :value="scope.row.status" />
+				</template>
+			</el-table-column>
+			<el-table-column label="创建时间" align="center" prop="createTime">
+				<template slot-scope="scope">
+					<span>{{ parseTime(scope.row.createTime) }}</span>
+				</template>
+			</el-table-column>
+			<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+				<template slot-scope="scope">
+					<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:menu:edit']">修改</el-button>
+					<el-button size="mini" type="text" icon="el-icon-plus" @click="handleAdd(scope.row)" v-hasPermi="['system:menu:add']">新增</el-button>
+					<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['system:menu:remove']">删除</el-button>
+				</template>
+			</el-table-column>
+		</el-table>
 
-    <!-- 添加或修改菜单对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="680px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
-        <el-row>
-          <el-col :span="24">
-            <el-form-item label="上级菜单">
-              <treeselect
-                v-model="form.parentId"
-                :options="menuOptions"
-                :normalizer="normalizer"
-                :show-count="true"
-                placeholder="选择上级菜单"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24">
-            <el-form-item label="菜单类型" prop="menuType">
-              <el-radio-group v-model="form.menuType">
-                <el-radio label="M">目录</el-radio>
-                <el-radio label="C">菜单</el-radio>
-                <el-radio label="F">按钮</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="24" v-if="form.menuType != 'F'">
-            <el-form-item label="菜单图标">
-              <el-popover
-                placement="bottom-start"
-                width="460"
-                trigger="click"
-                @show="$refs['iconSelect'].reset()"
-              >
-                <IconSelect ref="iconSelect" @selected="selected" />
-                <el-input slot="reference" v-model="form.icon" placeholder="点击选择图标" readonly>
-                  <svg-icon
-                    v-if="form.icon"
-                    slot="prefix"
-                    :icon-class="form.icon"
-                    class="el-input__icon"
-                    style="height: 32px;width: 16px;"
-                  />
-                  <i v-else slot="prefix" class="el-icon-search el-input__icon" />
-                </el-input>
-              </el-popover>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="菜单名称" prop="menuName">
-              <el-input v-model="form.menuName" placeholder="请输入菜单名称" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="显示排序" prop="orderNum">
-              <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
-              <span slot="label">
-                <el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
-                <i class="el-icon-question"></i>
-                </el-tooltip>
-                是否外链
-              </span>
-              <el-radio-group v-model="form.isFrame">
-                <el-radio label="0">是</el-radio>
-                <el-radio label="1">否</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item prop="path">
-              <span slot="label">
-                <el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
-                <i class="el-icon-question"></i>
-                </el-tooltip>
-                路由地址
-              </span>
-              <el-input v-model="form.path" placeholder="请输入路由地址" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12" v-if="form.menuType == 'C'">
-            <el-form-item prop="component">
-              <span slot="label">
-                <el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
-                <i class="el-icon-question"></i>
-                </el-tooltip>
-                组件路径
-              </span>
-              <el-input v-model="form.component" placeholder="请输入组件路径" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12" v-if="form.menuType != 'M'">
-            <el-form-item>
-              <el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
-              <span slot="label">
-                <el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
-                <i class="el-icon-question"></i>
-                </el-tooltip>
-                权限字符
-              </span>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12" v-if="form.menuType == 'C'">
-            <el-form-item>
-              <el-input v-model="form.query" placeholder="请输入路由参数" maxlength="255" />
-              <span slot="label">
-                <el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
-                <i class="el-icon-question"></i>
-                </el-tooltip>
-                路由参数
-              </span>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12" v-if="form.menuType == 'C'">
-            <el-form-item>
-              <span slot="label">
-                <el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
-                <i class="el-icon-question"></i>
-                </el-tooltip>
-                是否缓存
-              </span>
-              <el-radio-group v-model="form.isCache">
-                <el-radio label="0">缓存</el-radio>
-                <el-radio label="1">不缓存</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
-              <span slot="label">
-                <el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
-                <i class="el-icon-question"></i>
-                </el-tooltip>
-                显示状态
-              </span>
-              <el-radio-group v-model="form.visible">
-                <el-radio
-                  v-for="dict in dict.type.sys_show_hide"
-                  :key="dict.value"
-                  :label="dict.value"
-                >{{dict.label}}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12" v-if="form.menuType != 'F'">
-            <el-form-item>
-              <span slot="label">
-                <el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
-                <i class="el-icon-question"></i>
-                </el-tooltip>
-                菜单状态
-              </span>
-              <el-radio-group v-model="form.status">
-                <el-radio
-                  v-for="dict in dict.type.sys_normal_disable"
-                  :key="dict.value"
-                  :label="dict.value"
-                >{{dict.label}}</el-radio>
-              </el-radio-group>
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
-      </div>
-    </el-dialog>
-  </div>
+		<!-- 添加或修改菜单对话框 -->
+		<el-dialog :title="title" :visible.sync="open" width="680px" append-to-body>
+			<el-form ref="form" :model="form" :rules="rules" label-width="100px">
+				<el-row>
+					<el-col :span="24">
+						<el-form-item label="上级菜单">
+							<treeselect v-model="form.parentId" :options="menuOptions" :normalizer="normalizer" :show-count="true" placeholder="选择上级菜单" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="菜单类型" prop="menuType">
+							<el-radio-group v-model="form.menuType">
+								<el-radio label="M">目录</el-radio>
+								<el-radio label="C">菜单</el-radio>
+								<el-radio label="F">按钮</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="24" v-if="form.menuType != 'F'">
+						<el-form-item label="菜单图标">
+							<el-popover placement="bottom-start" width="460" trigger="click" @show="$refs['iconSelect'].reset()">
+								<IconSelect ref="iconSelect" @selected="selected" />
+								<el-input slot="reference" v-model="form.icon" placeholder="点击选择图标" readonly>
+									<svg-icon v-if="form.icon" slot="prefix" :icon-class="form.icon" class="el-input__icon" style="height: 32px; width: 16px" />
+									<i v-else slot="prefix" class="el-icon-search el-input__icon" />
+								</el-input>
+							</el-popover>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="菜单名称" prop="menuName">
+							<el-input v-model="form.menuName" placeholder="请输入菜单名称" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="显示排序" prop="orderNum">
+							<el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType != 'F'">
+						<el-form-item>
+							<span slot="label">
+								<el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
+									<i class="el-icon-question"></i>
+								</el-tooltip>
+								是否外链
+							</span>
+							<el-radio-group v-model="form.isFrame">
+								<el-radio label="0">是</el-radio>
+								<el-radio label="1">否</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType != 'F'">
+						<el-form-item prop="path">
+							<span slot="label">
+								<el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
+									<i class="el-icon-question"></i>
+								</el-tooltip>
+								路由地址
+							</span>
+							<el-input v-model="form.path" placeholder="请输入路由地址" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType == 'C'">
+						<el-form-item prop="component">
+							<span slot="label">
+								<el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
+									<i class="el-icon-question"></i>
+								</el-tooltip>
+								组件路径
+							</span>
+							<el-input v-model="form.component" placeholder="请输入组件路径" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType != 'M'">
+						<el-form-item>
+							<el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
+							<span slot="label">
+								<el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
+									<i class="el-icon-question"></i>
+								</el-tooltip>
+								权限字符
+							</span>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType == 'C'">
+						<el-form-item>
+							<el-input v-model="form.query" placeholder="请输入路由参数" maxlength="255" />
+							<span slot="label">
+								<el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
+									<i class="el-icon-question"></i>
+								</el-tooltip>
+								路由参数
+							</span>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType == 'C'">
+						<el-form-item>
+							<span slot="label">
+								<el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
+									<i class="el-icon-question"></i>
+								</el-tooltip>
+								是否缓存
+							</span>
+							<el-radio-group v-model="form.isCache">
+								<el-radio label="0">缓存</el-radio>
+								<el-radio label="1">不缓存</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType != 'F'">
+						<el-form-item>
+							<span slot="label">
+								<el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
+									<i class="el-icon-question"></i>
+								</el-tooltip>
+								显示状态
+							</span>
+							<el-radio-group v-model="form.visible">
+								<el-radio v-for="dict in dict.type.sys_show_hide" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12" v-if="form.menuType != 'F'">
+						<el-form-item>
+							<span slot="label">
+								<el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
+									<i class="el-icon-question"></i>
+								</el-tooltip>
+								菜单状态
+							</span>
+							<el-radio-group v-model="form.status">
+								<el-radio v-for="dict in dict.type.sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+							</el-radio-group>
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<div slot="footer" class="dialog-footer">
+				<el-button type="primary" @click="submitForm">确 定</el-button>
+				<el-button @click="cancel">取 消</el-button>
+			</div>
+		</el-dialog>
+	</div>
 </template>
 
 <script>
-import { listMenu, getMenu, delMenu, addMenu, updateMenu } from "@/api/system/menu";
-import Treeselect from "@riophae/vue-treeselect";
-import "@riophae/vue-treeselect/dist/vue-treeselect.css";
-import IconSelect from "@/components/IconSelect";
+	import { listMenu, getMenu, delMenu, addMenu, updateMenu } from '@/api/system/menu'
+	import Treeselect from '@riophae/vue-treeselect'
+	import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+	import IconSelect from '@/components/IconSelect'
 
-export default {
-  name: "Menu",
-  dicts: ['sys_show_hide', 'sys_normal_disable'],
-  components: { Treeselect, IconSelect },
-  data() {
-    return {
-      // 遮罩层
-      loading: true,
-      // 显示搜索条件
-      showSearch: true,
-      // 菜单表格树数据
-      menuList: [],
-      // 菜单树选项
-      menuOptions: [],
-      // 弹出层标题
-      title: "",
-      // 是否显示弹出层
-      open: false,
-      // 是否展开,默认全部折叠
-      isExpandAll: false,
-      // 重新渲染表格状态
-      refreshTable: true,
-      // 查询参数
-      queryParams: {
-        menuName: undefined,
-        visible: undefined
-      },
-      // 表单参数
-      form: {},
-      // 表单校验
-      rules: {
-        menuName: [
-          { required: true, message: "菜单名称不能为空", trigger: "blur" }
-        ],
-        orderNum: [
-          { required: true, message: "菜单顺序不能为空", trigger: "blur" }
-        ],
-        path: [
-          { required: true, message: "路由地址不能为空", trigger: "blur" }
-        ]
-      }
-    };
-  },
-  created() {
-    this.getList();
-  },
-  methods: {
-    // 选择图标
-    selected(name) {
-      this.form.icon = name;
-    },
-    /** 查询菜单列表 */
-    getList() {
-      this.loading = true;
-      listMenu(this.queryParams).then(response => {
-        this.menuList = this.handleTree(response.data, "menuId");
-        this.loading = false;
-      });
-    },
-    /** 转换菜单数据结构 */
-    normalizer(node) {
-      if (node.children && !node.children.length) {
-        delete node.children;
-      }
-      return {
-        id: node.menuId,
-        label: node.menuName,
-        children: node.children
-      };
-    },
-    /** 查询菜单下拉树结构 */
-    getTreeselect() {
-      listMenu().then(response => {
-        this.menuOptions = [];
-        const menu = { menuId: 0, menuName: '主类目', children: [] };
-        menu.children = this.handleTree(response.data, "menuId");
-        this.menuOptions.push(menu);
-      });
-    },
-    // 取消按钮
-    cancel() {
-      this.open = false;
-      this.reset();
-    },
-    // 表单重置
-    reset() {
-      this.form = {
-        menuId: undefined,
-        parentId: 0,
-        menuName: undefined,
-        icon: undefined,
-        menuType: "M",
-        orderNum: undefined,
-        isFrame: "1",
-        isCache: "0",
-        visible: "0",
-        status: "0"
-      };
-      this.resetForm("form");
-    },
-    /** 搜索按钮操作 */
-    handleQuery() {
-      this.getList();
-    },
-    /** 重置按钮操作 */
-    resetQuery() {
-      this.resetForm("queryForm");
-      this.handleQuery();
-    },
-    /** 新增按钮操作 */
-    handleAdd(row) {
-      this.reset();
-      this.getTreeselect();
-      if (row != null && row.menuId) {
-        this.form.parentId = row.menuId;
-      } else {
-        this.form.parentId = 0;
-      }
-      this.open = true;
-      this.title = "添加菜单";
-    },
-    /** 展开/折叠操作 */
-    toggleExpandAll() {
-      this.refreshTable = false;
-      this.isExpandAll = !this.isExpandAll;
-      this.$nextTick(() => {
-        this.refreshTable = true;
-      });
-    },
-    /** 修改按钮操作 */
-    handleUpdate(row) {
-      this.reset();
-      this.getTreeselect();
-      getMenu(row.menuId).then(response => {
-        this.form = response.data;
-        this.open = true;
-        this.title = "修改菜单";
-      });
-    },
-    /** 提交按钮 */
-    submitForm: function() {
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-          if (this.form.menuId != undefined) {
-            updateMenu(this.form).then(response => {
-              this.$modal.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
-            });
-          } else {
-            addMenu(this.form).then(response => {
-              this.$modal.msgSuccess("新增成功");
-              this.open = false;
-              this.getList();
-            });
-          }
-        }
-      });
-    },
-    /** 删除按钮操作 */
-    handleDelete(row) {
-      this.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?').then(function() {
-        return delMenu(row.menuId);
-      }).then(() => {
-        this.getList();
-        this.$modal.msgSuccess("删除成功");
-      }).catch(() => {});
-    }
-  }
-};
+	export default {
+		name: 'Menu',
+		dicts: ['sys_show_hide', 'sys_normal_disable'],
+		components: { Treeselect, IconSelect },
+		data() {
+			return {
+				// 遮罩层
+				loading: true,
+				// 显示搜索条件
+				showSearch: true,
+				// 菜单表格树数据
+				menuList: [],
+				// 菜单树选项
+				menuOptions: [],
+				// 弹出层标题
+				title: '',
+				// 是否显示弹出层
+				open: false,
+				// 是否展开,默认全部折叠
+				isExpandAll: false,
+				// 重新渲染表格状态
+				refreshTable: true,
+				// 查询参数
+				queryParams: {
+					menuName: undefined,
+					visible: undefined
+				},
+				// 表单参数
+				form: {},
+				// 表单校验
+				rules: {
+					menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
+					orderNum: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
+					path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }]
+				}
+			}
+		},
+		created() {
+			this.getList()
+		},
+		methods: {
+			// 选择图标
+			selected(name) {
+				this.form.icon = name
+			},
+			/** 查询菜单列表 */
+			getList() {
+				this.loading = true
+				listMenu(this.queryParams).then((response) => {
+					this.menuList = this.handleTree(response.data, 'menuId')
+					this.loading = false
+				})
+			},
+			/** 转换菜单数据结构 */
+			normalizer(node) {
+				if (node.children && !node.children.length) {
+					delete node.children
+				}
+				return {
+					id: node.menuId,
+					label: node.menuName,
+					children: node.children
+				}
+			},
+			/** 查询菜单下拉树结构 */
+			getTreeselect() {
+				listMenu().then((response) => {
+					this.menuOptions = []
+					const menu = { menuId: 0, menuName: '主类目', children: [] }
+					menu.children = this.handleTree(response.data, 'menuId')
+					this.menuOptions.push(menu)
+				})
+			},
+			// 取消按钮
+			cancel() {
+				this.open = false
+				this.reset()
+			},
+			// 表单重置
+			reset() {
+				this.form = {
+					menuId: undefined,
+					parentId: 0,
+					menuName: undefined,
+					icon: undefined,
+					menuType: 'M',
+					orderNum: undefined,
+					isFrame: '1',
+					isCache: '0',
+					visible: '0',
+					status: '0'
+				}
+				this.resetForm('form')
+			},
+			/** 搜索按钮操作 */
+			handleQuery() {
+				this.getList()
+			},
+			/** 重置按钮操作 */
+			resetQuery() {
+				this.resetForm('queryForm')
+				this.handleQuery()
+			},
+			/** 新增按钮操作 */
+			handleAdd(row) {
+				this.reset()
+				this.getTreeselect()
+				if (row != null && row.menuId) {
+					this.form.parentId = row.menuId
+				} else {
+					this.form.parentId = 0
+				}
+				this.open = true
+				this.title = '添加菜单'
+			},
+			/** 展开/折叠操作 */
+			toggleExpandAll() {
+				this.refreshTable = false
+				this.isExpandAll = !this.isExpandAll
+				this.$nextTick(() => {
+					this.refreshTable = true
+				})
+			},
+			/** 修改按钮操作 */
+			handleUpdate(row) {
+				this.reset()
+				this.getTreeselect()
+				getMenu(row.menuId).then((response) => {
+					this.form = response.data
+					this.open = true
+					this.title = '修改菜单'
+				})
+			},
+			/** 提交按钮 */
+			submitForm: function () {
+				this.$refs['form'].validate((valid) => {
+					if (valid) {
+						if (this.form.menuId != undefined) {
+							updateMenu(this.form).then((response) => {
+								this.$modal.msgSuccess('修改成功')
+								this.open = false
+								this.getList()
+							})
+						} else {
+							addMenu(this.form).then((response) => {
+								this.$modal.msgSuccess('新增成功')
+								this.open = false
+								this.getList()
+							})
+						}
+					}
+				})
+			},
+			/** 删除按钮操作 */
+			handleDelete(row) {
+				this.$modal
+					.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?')
+					.then(function () {
+						return delMenu(row.menuId)
+					})
+					.then(() => {
+						this.getList()
+						this.$modal.msgSuccess('删除成功')
+					})
+					.catch(() => {})
+			}
+		}
+	}
 </script>

+ 64 - 13
src/views/system/tableHead/index.vue

@@ -28,7 +28,7 @@
 						<el-button type="primary" plain size="small " style="margin: 0 0 10px 3px" @click="handleAdd" v-hasPermi="['system:tableHead:addTable']">新增</el-button>
 					</template>
 					<el-table v-loading="loading" :data="tableHeadList">
-						<el-table-column type="index" width="55" align="center" label="序号" />
+						<el-table-column type="index" width="100" align="center" label="序号" />
 						<el-table-column label="列名称" prop="columnName" width="120" />
 						<el-table-column label="前端列名" prop="uiColumn" :show-overflow-tooltip="true" width="150" />
 						<el-table-column label="后端列名" prop="apiColum" :show-overflow-tooltip="true" width="150" />
@@ -64,9 +64,9 @@
 				<el-form-item label="后端列名" prop="apiColum">
 					<el-input v-model="form.apiColum" placeholder="请输入列名称" />
 				</el-form-item>
-				<!-- <el-form-item label="排序" prop="apiColum">
-					<el-input v-model="form.apiColum" placeholder="请输入列名称" />
-				</el-form-item> -->
+				<el-form-item label="排序" prop="orderNum">
+					<el-input v-model.number="form.orderNum" placeholder="请输入排序值" />
+				</el-form-item>
 				<el-form-item label="是否必须显示" prop="required">
 					<el-radio-group v-model="form.required">
 						<el-radio v-for="dict in dict.type.sys_tableHead_required" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
@@ -116,13 +116,18 @@
 					columnName: '',
 					uiColumn: '',
 					apiColum: '',
-					required: '0'
+					required: '0',
+					orderNum: ''
 				},
 				//表单验证
 				rules: {
-					columnName: [{ required: true, message: '列名称不能为空' }],
-					uiColumn: [{ required: true, message: '前端列名不能为空' }],
-					apiColum: [{ required: true, message: '后端列名不能为空' }]
+					columnName: [{ required: true, message: '列名称不能为空', trigger: 'submit' }],
+					uiColumn: [{ required: true, message: '前端列名不能为空', trigger: 'submit' }],
+					apiColum: [{ required: true, message: '后端列名不能为空', trigger: 'submit' }],
+					orderNum: [
+						{ required: true, message: '排序值不能为空', trigger: 'submit' },
+						{ type: 'number', message: '排序值只能为数字', trigger: 'submit' }
+					]
 				}
 			}
 		},
@@ -174,11 +179,20 @@
 				tableHeadList({ menuId: this.menuId, ...this.queryParams }).then((res) => {
 					if (res.code == 200) {
 						this.total = res.total
-						this.tableHeadList = res.rows
+						this.tableHeadList = res.rows.sort((a, b) => {
+							return a.orderNum - b.orderNum
+						})
+					}
+				})
+			},
+			handleStatusChange(data) {
+				tableHeadUpd(data).then((res) => {
+					if (res.code == 200) {
+						this.$msg({ message: '修改成功' })
+						this.updateData(data)
 					}
 				})
 			},
-			handleStatusChange() {},
 			//新增表头数据
 			handleAdd() {
 				this.form.menuId = this.menuId
@@ -206,12 +220,18 @@
 			submitForm() {
 				this.$refs.form.validate((valid) => {
 					if (valid) {
+						let checkFiled = this.checkName(this.form)
+						if (checkFiled) {
+							this.$msg({ type: 'error', message: checkFiled })
+							return
+						}
 						if (this.form.id) {
 							tableHeadUpd(this.form).then((res) => {
 								if (res.code == 200) {
 									this.$msg({ message: '修改成功' })
 									this.updateData(this.form)
-									this.cancel()
+								} else {
+									this.$msg({ type: 'error', message: res.msg })
 								}
 							})
 						} else {
@@ -220,7 +240,8 @@
 									this.$msg({ message: '添加成功' })
 									this.form.id = res.data
 									this.updateData(this.form)
-									this.cancel()
+								} else {
+									this.$msg({ type: 'error', message: res.msg })
 								}
 							})
 						}
@@ -243,18 +264,48 @@
 							...item
 						}
 					})
+					this.tableHeadList.sort((a, b) => {
+						return a.orderNum - b.orderNum
+					})
 				} else {
 					this.tableHeadList.push(data)
 				}
+				this.cancel()
 			},
 			//弹窗取消
 			cancel() {
 				this.resetForm()
 				this.open = false
 			},
+			//重名检测
+			checkName(data) {
+				let checkColumnName = this.tableHeadList.find((item) => item.columnName == data.columnName)
+				let checkUiColumn = this.tableHeadList.find((item) => item.uiColumn == data.uiColumn)
+				let checkApiColum = this.tableHeadList.find((item) => item.apiColum == data.apiColum)
+				if (checkColumnName) {
+					return '列名称重复'
+				}
+				if (checkUiColumn) {
+					return '前端列名重复'
+				}
+				if (checkApiColum) {
+					return '后端列名重复'
+				}
+				return false
+			},
 			//表单重置
 			resetForm() {
-				this.$refs.form.resetFields()
+				if (this.$refs.form) {
+					this.$refs.form.clearValidate()
+				}
+				this.form = {
+					menuId: null,
+					columnName: '',
+					uiColumn: '',
+					apiColum: '',
+					required: '0',
+					orderNum: ''
+				}
 			}
 		}
 	}