refactor: eslint代码检查优化

Former-commit-id: 4c11b5d0cdd10f28148cf3d9b593f85e082cdc51
This commit is contained in:
郝先瑞
2022-04-15 00:45:06 +08:00
parent 15022f51b9
commit dd93144788
69 changed files with 820 additions and 1774 deletions

View File

@@ -18,7 +18,7 @@ export function listCategories(queryParams:object){
* *
* @param queryParams * @param queryParams
*/ */
export function listCascadeCategories(queryParams:object) { export function listCascadeCategories(queryParams?:object) {
return request({ return request({
url: '/mall-pms/api/v1/categories/cascade', url: '/mall-pms/api/v1/categories/cascade',
method: 'get', method: 'get',

View File

@@ -1,4 +1,4 @@
import { GoodsPageResult, GoodsQueryParam } from '@/types' import { GoodsDetail, GoodsPageResult, GoodsQueryParam } from '@/types'
import request from '@/utils/request' import request from '@/utils/request'
import { AxiosPromise } from 'axios' import { AxiosPromise } from 'axios'
@@ -20,7 +20,7 @@ export function listGoodsPages(queryParams: GoodsQueryParam):AxiosPromise<GoodsP
* *
* @param id * @param id
*/ */
export function getGoodsFormDetail(id: number) { export function getGoodsDetail(id: string):AxiosPromise<GoodsDetail> {
return request({ return request({
url: '/mall-pms/api/v1/goods/' + id, url: '/mall-pms/api/v1/goods/' + id,
method: 'get' method: 'get'

View File

@@ -6,7 +6,7 @@ import request from '@/utils/request'
* @param file * @param file
*/ */
export function uploadFile(file: File) { export function uploadFile(file: File) {
let formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', file)
return request( return request(
{ {

View File

@@ -98,7 +98,7 @@ export function listRoleMenuIds(roleId: number):AxiosPromise<number[]> {
* @param roleId * @param roleId
* @param menuIds * @param menuIds
*/ */
export function updateRoleMenu(roleId: number, menuIds: Array<Number>) { export function updateRoleMenu(roleId: number, menuIds: Array<number>) {
return request({ return request({
url: '/youlai-admin/api/v1/roles/' + roleId + '/menus', url: '/youlai-admin/api/v1/roles/' + roleId + '/menus',
method: 'put', method: 'put',

View File

@@ -1,6 +1,6 @@
import request from "@/utils/request"; import request from "@/utils/request";
import { AxiosPromise } from "axios"; import { AxiosPromise } from "axios";
import { UserFormData, UserImportFormData, UserInfo, UserPageResult, UserQueryParam } from "@/types"; import { UserFormData, UserInfo, UserPageResult, UserQueryParam } from "@/types";
/** /**
* 登录成功后获取用户信息(昵称、头像、权限集合和角色集合) * 登录成功后获取用户信息(昵称、头像、权限集合和角色集合)
@@ -113,6 +113,7 @@ export function exportUser(queryParams: UserQueryParam) {
return request({ return request({
url: '/youlai-admin/api/v1/users/_export', url: '/youlai-admin/api/v1/users/_export',
method: 'get', method: 'get',
params:queryParams,
responseType: "arraybuffer" responseType: "arraybuffer"
}) })
} }
@@ -123,7 +124,7 @@ export function exportUser(queryParams: UserQueryParam) {
* @param file * @param file
*/ */
export function importUser(deptId: number, roleIds: string, file: File) { export function importUser(deptId: number, roleIds: string, file: File) {
let formData = new FormData() const formData = new FormData()
formData.append('file', file) formData.append('file', file)
formData.append('deptId',deptId.toString()) formData.append('deptId',deptId.toString())
formData.append('roleIds',roleIds) formData.append('roleIds',roleIds)

9
src/components.d.ts vendored Normal file
View File

@@ -0,0 +1,9 @@
// 全局组件类型声明
import Pagination from "@/components/Pagination/index.vue";
declare module "@vue/runtime-core" {
export interface GlobalComponents {
Pagination: typeof Pagination;
}
}
export {}

View File

@@ -1,13 +1,7 @@
<template> <template>
<div class="icon-body"> <div class="icon-body">
<el-input <el-input v-model="iconName" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons"
v-model="iconName" @input="filterIcons">
style="position: relative;"
clearable
placeholder="请输入图标名称"
@clear="filterIcons"
@input="filterIcons"
>
<template #suffix><i class="el-icon-search el-input__icon" /></template> <template #suffix><i class="el-icon-search el-input__icon" /></template>
</el-input> </el-input>
<div class="icon-list"> <div class="icon-list">
@@ -19,11 +13,11 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { ref} from "vue"; import { ref } from "vue";
import SvgIcon from '@/components/SvgIcon/index.vue'; import SvgIcon from '@/components/SvgIcon/index.vue';
let icons = [] const icons = [] as string[]
const modules = import.meta.glob('../../assets/icons/svg/*.svg'); const modules = import.meta.glob('../../assets/icons/svg/*.svg');
for (const path in modules) { for (const path in modules) {
const p = path.split('assets/icons/svg/')[1].split('.svg')[0]; const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
@@ -42,7 +36,7 @@ function filterIcons() {
} }
} }
function selectedIcon(name) { function selectedIcon(name: string) {
emit('selected', name) emit('selected', name)
document.body.click() document.body.click()
} }
@@ -61,9 +55,11 @@ defineExpose({
.icon-body { .icon-body {
width: 100%; width: 100%;
padding: 10px; padding: 10px;
.icon-list { .icon-list {
height: 200px; height: 200px;
overflow-y: scroll; overflow-y: scroll;
div { div {
height: 30px; height: 30px;
line-height: 30px; line-height: 30px;
@@ -72,6 +68,7 @@ defineExpose({
width: 33%; width: 33%;
float: left; float: left;
} }
span { span {
display: inline-block; display: inline-block;
vertical-align: -0.15em; vertical-align: -0.15em;

View File

@@ -1,26 +1,19 @@
<template> <template>
<div :class="{'hidden':hidden}" class="pagination-container"> <div :class="{ 'hidden': hidden }" class="pagination-container">
<el-pagination <el-pagination :background="background" v-model:current-page="currentPage" v-model:page-size="pageSize"
:background="background" :layout="layout" :page-sizes="pageSizes" :total="total" @size-change="handleSizeChange"
v-model:current-page="currentPage" @current-change="handleCurrentChange" />
v-model:page-size="pageSize"
:layout="layout"
:page-sizes="pageSizes"
:total="total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import {computed} from "vue"; import { computed, PropType } from "vue";
import {scrollTo} from '@/utils/scroll-to' import { scrollTo } from '@/utils/scroll-to'
const props = defineProps({ const props = defineProps({
total: { total: {
required: true, required: true,
type: Number, type: Number as PropType<number>,
default: 0 default: 0
}, },
page: { page: {
@@ -32,7 +25,7 @@ const props = defineProps({
default: 20 default: 20
}, },
pageSizes: { pageSizes: {
type: Array, type: Array as PropType<number[]>,
default() { default() {
return [10, 20, 30, 50] return [10, 20, 30, 50]
} }
@@ -55,18 +48,16 @@ const props = defineProps({
} }
}) })
const emit = defineEmits(); const emit = defineEmits(["update:page", "update:limit", "pagination"]);
const currentPage = computed({ const currentPage = computed<number | undefined>({
get() { get: () => props.page,
return props.page set: (value) => {
}, emit('update:page', value)
set(val) {
emit('update:page', val)
} }
}) })
const pageSize = computed({ const pageSize = computed<number | undefined>({
get() { get() {
return props.limit return props.limit
}, },
@@ -75,15 +66,16 @@ const pageSize = computed({
} }
}) })
function handleSizeChange(val) { function handleSizeChange(val: number) {
emit('pagination', {page: currentPage, limit: val}) emit('pagination', { page: currentPage, limit: val })
if (props.autoScroll) { if (props.autoScroll) {
scrollTo(0, 800) scrollTo(0, 800)
} }
} }
function handleCurrentChange(val) { function handleCurrentChange(val: number) {
emit('pagination', {page: val, limit: props.pageSizes}) currentPage.value = val
emit('pagination', { page: val, limit: props.limit })
if (props.autoScroll) { if (props.autoScroll) {
scrollTo(0, 800) scrollTo(0, 800)
} }

View File

@@ -1,42 +1,41 @@
<template> <template>
<div ref="rightPanel" :class="{show:show}" class="rightPanel-container"> <div ref="rightPanel" :class="{ show: show }" class="rightPanel-container">
<div class="rightPanel-background"/> <div class="rightPanel-background" />
<div class="rightPanel"> <div class="rightPanel">
<div class="handle-button" :style="{'top':buttonTop+'px','background-color':theme}" @click="show=!show"> <div class="handle-button" :style="{ 'top': buttonTop + 'px', 'background-color': theme }" @click="show = !show">
<Close style="width: 1em; height: 1em;vertical-align: middle " v-show="show"/> <Close style="width: 1em; height: 1em;vertical-align: middle " v-show="show" />
<Setting style="width:1em; height:1em;vertical-align: middle " v-show="!show"/> <Setting style="width:1em; height:1em;vertical-align: middle " v-show="!show" />
</div> </div>
<div class="rightPanel-items"> <div class="rightPanel-items">
<slot/> <slot />
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, onBeforeUnmount, onMounted, ref, watch} from "vue"; import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
import {addClass, removeClass} from '@/utils/index' import { addClass, removeClass } from '@/utils/index'
import useStore from "@/store"; import useStore from "@/store";
// 图标依赖 // 图标依赖
import {Close, Setting} from '@element-plus/icons-vue' import { Close, Setting } from '@element-plus/icons-vue'
import {ElColorPicker} from "element-plus"; import { ElColorPicker } from "element-plus";
const {setting} =useStore() const { setting } = useStore()
const props = defineProps({ const theme = computed(() => setting.theme)
const show = ref(false)
defineProps({
buttonTop: { buttonTop: {
default: 250, default: 250,
type: Number type: Number
} }
}) })
const theme = computed(() =>setting.theme)
const show = ref(false)
watch(show, (value) => { watch(show, (value) => {
if (value) { if (value) {
addEventClick() addEventClick()

View File

@@ -1,12 +1,12 @@
<template> <template>
<div> <div>
<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle"/> <svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle" />
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import {useFullscreen} from '@vueuse/core' import { useFullscreen } from '@vueuse/core'
import SvgIcon from '@/components/SvgIcon/index.vue' import SvgIcon from '@/components/SvgIcon/index.vue'
const {isFullscreen, enter, toggle} = useFullscreen(); const { isFullscreen, toggle } = useFullscreen();
</script> </script>

View File

@@ -5,12 +5,8 @@
</div> </div>
<template #dropdown> <template #dropdown>
<el-dropdown-menu> <el-dropdown-menu>
<el-dropdown-item <el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="(size || 'default') == item.value"
v-for="item of sizeOptions" :command="item.value">
:key="item.value"
:disabled="(size || 'default') == item.value"
:command="item.value"
>
{{ item.label }} {{ item.label }}
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
@@ -20,8 +16,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from "vue"; import { ref, computed } from "vue";
import { useRoute, useRouter } from "vue-router";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import useStore from "@/store"; import useStore from "@/store";

View File

@@ -8,9 +8,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, nextTick, watch} from "vue"; import {computed, watch} from "vue";
import useStore from "@/store"; import useStore from "@/store";
import {useRoute, useRouter} from "vue-router";
import {localStorage} from "@/utils/storage"; import {localStorage} from "@/utils/storage";
// 参考连接:https://juejin.cn/post/7024025899813044232#heading-1 // 参考连接:https://juejin.cn/post/7024025899813044232#heading-1

View File

@@ -1,44 +1,27 @@
<template> <template>
<div class="el-tree-select"> <div class="el-tree-select">
<el-select <el-select style="width: 100%" v-model="modelValue" ref="treeSelect" :filterable="true" :clearable="true"
style="width: 100%" @clear="clearHandle" :filter-method="selectFilterData" :placeholder="placeholder">
v-model="modelValue"
ref="treeSelect"
:filterable="true"
:clearable="true"
@clear="clearHandle"
:filter-method="selectFilterData"
:placeholder="placeholder"
>
<el-option :value="modelValue" :label="valueTitle"> <el-option :value="modelValue" :label="valueTitle">
<el-tree <el-tree id="tree-option" ref="selectTree" :accordion="accordion" :data="options" :props="state.props"
id="tree-option" :node-key="state.props.value" :expand-on-click-node="false" :default-expanded-keys="defaultExpandedKey"
ref="selectTree" :filter-node-method="filterNode" @node-click="handleNodeClick"></el-tree>
:accordion="accordion"
:data="options"
:props="state.props"
:node-key="state.props.value"
:expand-on-click-node="false"
:default-expanded-keys="defaultExpandedKey"
:filter-node-method="filterNode"
@node-click="handleNodeClick"
></el-tree>
</el-option> </el-option>
</el-select> </el-select>
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { import {
ref, ref,
getCurrentInstance, getCurrentInstance,
nextTick, nextTick,
onMounted, onMounted,
computed, computed,
watch, watch
} from "vue"; } from "vue";
const { proxy } = getCurrentInstance(); const { proxy } = getCurrentInstance() as any;
const state = defineProps({ const state = defineProps({
// 配置项 // 配置项
@@ -67,7 +50,9 @@ const state = defineProps({
// 数据源 // 数据源
options: { options: {
type: Array, type: Array,
default: [], default: () => {
return []
}
}, },
// 提示文字 // 提示文字
placeholder: { placeholder: {
@@ -87,9 +72,9 @@ const modelValue = computed({
}, },
}); });
const valueTitle = ref(""); const valueTitle = ref("");
const defaultExpandedKey = ref([]); const defaultExpandedKey = ref([] as any[]);
function handleNodeClick(node) { function handleNodeClick(node: any) {
valueTitle.value = node[state.props.label]; valueTitle.value = node[state.props.label];
modelValue.value = node[state.props.value]; modelValue.value = node[state.props.value];
defaultExpandedKey.value = []; defaultExpandedKey.value = [];
@@ -97,11 +82,11 @@ function handleNodeClick(node) {
selectFilterData(""); selectFilterData("");
} }
function selectFilterData(val) { function selectFilterData(val: any) {
proxy.$refs.selectTree.filter(val); proxy.$refs.selectTree.filter(val);
} }
function filterNode(value, data) { function filterNode(value: any, data: any) {
if (!value) return true; if (!value) return true;
return data[state.props["label"]].indexOf(value) !== -1; return data[state.props["label"]].indexOf(value) !== -1;
} }

View File

@@ -28,18 +28,16 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from "vue"; import { computed } from "vue";
import { Plus, Close } from "@element-plus/icons-vue"; import { Plus, Close } from "@element-plus/icons-vue";
import { import {
ElMessage, ElMessage,
ElUpload, ElUpload,
UploadFile,
UploadRawFile, UploadRawFile,
UploadRequestOptions, UploadRequestOptions,
} from "element-plus"; } from "element-plus";
import { uploadFile, deleteFile } from "@/api/system/file"; import { uploadFile, deleteFile } from "@/api/system/file";
const uploadRef = ref(ElUpload);
const emit = defineEmits(["update:modelValue"]); const emit = defineEmits(["update:modelValue"]);
const props = defineProps({ const props = defineProps({
@@ -89,10 +87,9 @@ function handleRemove(fileUrl?: string) {
} }
/** /**
* 在 before-upload 钩子中限制用户上传文件的格式和大小 * 在 before-upload 钩子中限制用户上传文件的格式和大小
*
*/ */
function handleBeforeUpload(file: UploadRawFile) { function handleBeforeUpload(file: UploadRawFile) {
const isJPG = file.type === "image/jpeg"; // const isJPG = file.type === "image/jpeg";
const isLt2M = file.size / 1024 / 1024 < 2; const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) { if (!isLt2M) {

View File

@@ -1,28 +1,19 @@
<template> <template>
<div style="border: 1px solid #ccc"> <div style="border: 1px solid #ccc">
<!-- 工具栏 --> <!-- 工具栏 -->
<Toolbar <Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" style="border-bottom: 1px solid #ccc" :mode="mode" />
:editorId="editorId"
:defaultConfig="toolbarConfig"
style="border-bottom: 1px solid #ccc"
/>
<!-- 编辑器 --> <!-- 编辑器 -->
<Editor <Editor :defaultConfig="editorConfig" v-model="defaultHtml" @onChange="handleChange"
:editorId="editorId" style="height: 500px; overflow-y: hidden;" :mode="mode" @onCreated="handleCreated" />
:defaultConfig="editorConfig"
:defaultHtml="defaultHtml"
@onChange="handleChange"
style="height: 500px; overflow-y: hidden;"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {computed, onBeforeUnmount, reactive, toRefs} from 'vue' import { onBeforeUnmount, shallowRef, reactive, toRefs} from 'vue'
import {Editor, Toolbar, getEditor, removeEditor} from '@wangeditor/editor-for-vue' import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
// API 引用 // API 引用
import {uploadFile} from "@/api/system/file"; import { uploadFile } from "@/api/system/file";
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
@@ -33,16 +24,18 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue']); const emit = defineEmits(['update:modelValue']);
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
const state = reactive({ const state = reactive({
editorId: `w-e-${Math.random().toString().slice(-5)}`, //【注意】编辑器 id ,要全局唯一
toolbarConfig: {}, toolbarConfig: {},
editorConfig: { editorConfig: {
placeholder: '请输入内容...', placeholder: '请输入内容...',
MENU_CONF: { MENU_CONF: {
uploadImage: { uploadImage: {
// 自定义图片上传 // 自定义图片上传
// @link https://www.wangeditor.com/v5/guide/menu-config.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%8A%9F%E8%83%BD async customUpload(file: any, insertFn: any) {
async customUpload(file:any, insertFn:any) { console.log("上传图片")
uploadFile(file).then(response => { uploadFile(file).then(response => {
const url = response.data const url = response.data
insertFn(url) insertFn(url)
@@ -51,23 +44,28 @@ const state = reactive({
} }
} }
}, },
defaultHtml: props.modelValue defaultHtml: props.modelValue,
mode: 'default'
}) })
const {editorId, toolbarConfig, editorConfig,defaultHtml} = toRefs(state) const { toolbarConfig, editorConfig, defaultHtml, mode } = toRefs(state)
function handleChange(editor:any) { const handleCreated = (editor: any) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
function handleChange(editor: any) {
emit('update:modelValue', editor.getHtml()) emit('update:modelValue', editor.getHtml())
} }
// 组件销毁时,也及时销毁编辑器 // 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => { onBeforeUnmount(() => {
const editor = getEditor(state.editorId) const editor = editorRef.value
if (editor == null) return if (editor == null) return
editor.destroy() editor.destroy()
removeEditor(state.editorId)
}) })
</script> </script>
<style src="@wangeditor/editor/dist/css/style.css"></style> <style src="@wangeditor/editor/dist/css/style.css">
</style>

View File

@@ -65,7 +65,6 @@ import Hamburger from "@/components/Hamburger/index.vue";
import Screenfull from "@/components/Screenfull/index.vue"; import Screenfull from "@/components/Screenfull/index.vue";
import SizeSelect from "@/components/SizeSelect/index.vue"; import SizeSelect from "@/components/SizeSelect/index.vue";
import LangSelect from "@/components/LangSelect/index.vue"; import LangSelect from "@/components/LangSelect/index.vue";
import SvgIcon from "@/components/SvgIcon/index.vue";
// 图标依赖 // 图标依赖
import { CaretBottom } from "@element-plus/icons-vue"; import { CaretBottom } from "@element-plus/icons-vue";

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="sidebar-logo-container" :class="{'collapse':collapse}"> <div class="sidebar-logo-container" :class="{ 'collapse': isCollapse }">
<transition name="sidebarLogoFade"> <transition name="sidebarLogoFade">
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
<img v-if="logo" :src="logo" class="sidebar-logo"> <img v-if="logo" :src="logo" class="sidebar-logo">
@@ -14,17 +14,23 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue'; import { ref, reactive, toRefs } from 'vue';
const props = defineProps({ const props = defineProps({
collapse: { collapse: {
type: Boolean, type: Boolean,
required: true required: true
} }
}) })
const title=ref("vue3-element-admin") const state = reactive({
const logo=ref("https://s2.loli.net/2022/04/07/hyquWXELOoYvlP6.png") isCollapse: props.collapse
})
const { isCollapse } = toRefs(state)
const title = ref("vue3-element-admin")
const logo = ref("https://s2.loli.net/2022/04/07/hyquWXELOoYvlP6.png")
</script> </script>
@@ -32,10 +38,12 @@ const logo=ref("https://s2.loli.net/2022/04/07/hyquWXELOoYvlP6.png")
.sidebarLogoFade-enter-active { .sidebarLogoFade-enter-active {
transition: opacity 1.5s; transition: opacity 1.5s;
} }
.sidebarLogoFade-enter, .sidebarLogoFade-enter,
.sidebarLogoFade-leave-to { .sidebarLogoFade-leave-to {
opacity: 0; opacity: 0;
} }
.sidebar-logo-container { .sidebar-logo-container {
position: relative; position: relative;
width: 100%; width: 100%;

View File

@@ -32,8 +32,8 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import path from 'path-browserify'
import { ref} from "vue"; import { ref} from "vue";
import path from 'path-browserify'
import {isExternal} from '@/utils/validate' import {isExternal} from '@/utils/validate'
import AppLink from './Link.vue' import AppLink from './Link.vue'
@@ -83,7 +83,7 @@ function hasOneShowingChild(children = [] as any, parent: any) {
} }
return false return false
}; }
function resolvePath(routePath: string) { function resolvePath(routePath: string) {
if (isExternal(routePath)) { if (isExternal(routePath)) {

View File

@@ -25,7 +25,7 @@
<script setup lang="ts"> <script setup lang="ts">
import {computed, defineComponent} from "vue"; import {computed} from "vue";
import {useRoute} from 'vue-router' import {useRoute} from 'vue-router'
import SidebarItem from './SidebarItem.vue' import SidebarItem from './SidebarItem.vue'
@@ -44,7 +44,7 @@ const activeMenu = computed(() => {
const {meta, path} = route const {meta, path} = route
// if set path, the sidebar will highlight the path you set // if set path, the sidebar will highlight the path you set
if (meta.activeMenu) { if (meta.activeMenu) {
return meta.activeMenu return meta.activeMenu as string
} }
return path return path
}) })

View File

@@ -15,12 +15,11 @@ import {
computed, computed,
onMounted, onMounted,
onBeforeUnmount, onBeforeUnmount,
getCurrentInstance, getCurrentInstance
} from "vue"; } from "vue";
import { TagView } from "@/types"; import { TagView } from "@/types";
import useStore from "@/store"; import useStore from "@/store";
const emits = defineEmits();
const tagAndTagSpacing = ref(4); const tagAndTagSpacing = ref(4);
const scrollContainerRef = ref(null); const scrollContainerRef = ref(null);
@@ -29,9 +28,6 @@ const { tagsView } = useStore();
const visitedViews = computed(() => tagsView.visitedViews); const visitedViews = computed(() => tagsView.visitedViews);
const emitScroll = () => {
(emits as any)("scroll");
};
const { ctx } = getCurrentInstance() as any; const { ctx } = getCurrentInstance() as any;
const scrollWrapper = computed(() => { const scrollWrapper = computed(() => {

View File

@@ -20,7 +20,7 @@
class="el-icon-close" class="el-icon-close"
@click.prevent.stop="closeSelectedTag(tag)" @click.prevent.stop="closeSelectedTag(tag)"
> >
<close <Close
class="el-icon-close" class="el-icon-close"
style="width: 1em; height: 1em; vertical-align: middle" style="width: 1em; height: 1em; vertical-align: middle"
/> />

View File

@@ -5,16 +5,16 @@
class="drawer-bg" class="drawer-bg"
@click="handleClickOutside" @click="handleClickOutside"
/> />
<sidebar class="sidebar-container" /> <Sidebar class="sidebar-container" />
<div :class="{ hasTagsView: needTagsView }" class="main-container"> <div :class="{ hasTagsView: needTagsView }" class="main-container">
<div :class="{ 'fixed-header': fixedHeader }"> <div :class="{ 'fixed-header': fixedHeader }">
<navbar /> <navbar />
<tags-view v-if="needTagsView" /> <tags-view v-if="needTagsView" />
</div> </div>
<app-main /> <app-main />
<right-panel v-if="showSettings"> <RightPanel v-if="showSettings">
<settings /> <settings />
</right-panel> </RightPanel>
</div> </div>
</div> </div>
</template> </template>
@@ -28,7 +28,7 @@ import RightPanel from "@/components/RightPanel/index.vue";
import useStore from "@/store"; import useStore from "@/store";
const { width, height } = useWindowSize(); const { width } = useWindowSize();
const WIDTH = 992; const WIDTH = 992;
const { app, setting } = useStore(); const { app, setting } = useStore();

View File

@@ -62,7 +62,7 @@ const usePermissionStore = defineStore({
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
listRoutes().then(response => { listRoutes().then(response => {
const asyncRoutes = response.data const asyncRoutes = response.data
let accessedRoutes = filterAsyncRoutes(asyncRoutes, roles) const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
this.setRoutes(accessedRoutes) this.setRoutes(accessedRoutes)
resolve(accessedRoutes) resolve(accessedRoutes)
}).catch(error => { }).catch(error => {

View File

@@ -145,7 +145,7 @@ const useTagsViewStore = defineStore({
}) })
}) })
}, },
delAllViews(view: any) { delAllViews() {
return new Promise(resolve => { return new Promise(resolve => {
const affixTags = this.visitedViews.filter(tag => tag.meta?.affix) const affixTags = this.visitedViews.filter(tag => tag.meta?.affix)
this.visitedViews = affixTags this.visitedViews = affixTags

View File

@@ -23,8 +23,6 @@
--el-button-text-color: var(--el-color-white) !important; --el-button-text-color: var(--el-color-white) !important;
--el-button-bg-color: var(--el-color-primary) !important; --el-button-bg-color: var(--el-color-primary) !important;
--el-button-border-color: var(--el-color-primary) !important; --el-button-border-color: var(--el-color-primary) !important;
--el-button-hover-bg-color: var(--el-color-primary-light-2) !important;
--el-button-hover-border-color: var(--el-color-primary-light-2) !important;
--el-button-active-bg-color: var(--el-color-primary-dark) !important; --el-button-active-bg-color: var(--el-color-primary-dark) !important;
--el-button-active-border-color: var(--el-color-primary-dark) !important; --el-button-active-border-color: var(--el-color-primary-dark) !important;
} }
@@ -34,8 +32,6 @@
--el-button-bg-color: var(--el-color-primary-light-9)!important; --el-button-bg-color: var(--el-color-primary-light-9)!important;
--el-button-border-color: var(--el-color-primary-light-5)!important; --el-button-border-color: var(--el-color-primary-light-5)!important;
--el-button-hover-text-color: var(--el-color-white)!important; --el-button-hover-text-color: var(--el-color-white)!important;
--el-button-hover-bg-color: var(--el-color-primary)!important;
--el-button-hover-border-color: var(--el-color-primary)!important;
--el-button-active-text-color: var(--el-color-white)!important; --el-button-active-text-color: var(--el-color-white)!important;
--el-button-active-border-color: var(--el-color-primary)!important; --el-button-active-border-color: var(--el-color-primary)!important;
} }

View File

@@ -38,9 +38,7 @@ export interface OrderItem {
/** /**
* 订单分页项类型声明 * 订单分页项类型声明
*/ */
export interface OrderPageResult extends PageResult<Order[]> { export type OrderPageResult = PageResult<Order[]>
}
/** /**
* 订单表单类型声明 * 订单表单类型声明

View File

@@ -4,7 +4,7 @@ import { PageQueryParam, PageResult } from "../base"
* 品牌查询参数类型声明 * 品牌查询参数类型声明
*/ */
export interface BrandQueryParam extends PageQueryParam { export interface BrandQueryParam extends PageQueryParam {
name: String | undefined name?: string
} }
@@ -21,9 +21,7 @@ export interface BrandItem {
/** /**
* 品牌分页项类型声明 * 品牌分页项类型声明
*/ */
export interface BrandPageResult extends PageResult<BrandItem[]> { export type BrandPageResult = PageResult<BrandItem[]>
}
/** /**
* 品牌表单类型声明 * 品牌表单类型声明

View File

@@ -1,12 +1,11 @@
import { PageQueryParam, PageResult } from "../base" import { PageQueryParam, PageResult } from "../base"
/** /**
* 商品查询参数类型声明 * 商品查询参数类型声明
*/ */
export interface GoodsQueryParam extends PageQueryParam { export interface GoodsQueryParam extends PageQueryParam {
name: stirng | undefined, name?: stirng,
categoryId: number | undefined categoryId?: number
} }
/** /**
@@ -49,25 +48,25 @@ export interface SkuItem {
/** /**
* 商品分页项类型声明 * 商品分页项类型声明
*/ */
export interface GoodsPageResult extends PageResult<GoodsItem[]> { export type GoodsPageResult = PageResult<GoodsItem[]>
}
/** /**
* 商品表单数据类型声明 * 商品表单数据类型声明
*/ */
export interface GoodsFormData { export interface GoodsDetail {
id: number|undefined, id: string,
deptId: number, name: string,
username: string, categoryId: string,
nickname: string, brandId: string,
password: string, originPrice: number,
mobile: string, price: number,
email: string, picUrl: string,
gender: number, album: string[],
status: number, description: string,
remark: string, detail: string,
roleIds: number[] attrList: any[],
specList: any[],
skuList: any[]
} }

View File

@@ -4,7 +4,7 @@ import { PageQueryParam, PageResult } from "../base"
* 广告查询参数类型声明 * 广告查询参数类型声明
*/ */
export interface AdvertQueryParam extends PageQueryParam { export interface AdvertQueryParam extends PageQueryParam {
title: String | undefined title?: string
} }
/** /**
@@ -20,15 +20,13 @@ export interface AdvertItem {
/** /**
* 广告分页项类型声明 * 广告分页项类型声明
*/ */
export interface AdvertPageResult extends PageResult<AdvertItem[]> { export type AdvertPageResult = PageResult<AdvertItem[]>
}
/** /**
* 广告表单类型声明 * 广告表单类型声明
*/ */
export interface AdvertFormData { export interface AdvertFormData {
id: number | undefined; id?: number;
title: string; title: string;
picUrl: string; picUrl: string;
beginTime: string; beginTime: string;

View File

@@ -31,9 +31,7 @@ export interface ClientItem {
/** /**
* 客户端分页项类型声明 * 客户端分页项类型声明
*/ */
export interface ClientPageResult extends PageResult<ClientItem[]> { export type ClientPageResult = PageResult<ClientItem[]>
}
/** /**
* 客户端表单类型声明 * 客户端表单类型声明

View File

@@ -25,9 +25,7 @@ export interface Dict {
/** /**
* 字典分页项类型声明 * 字典分页项类型声明
*/ */
export interface DictPageResult extends PageResult<Dict[]> { export type DictPageResult = PageResult<Dict[]>
}
/** /**
* 字典表单类型声明 * 字典表单类型声明
@@ -71,16 +69,15 @@ export interface DictItem {
/** /**
* 字典分页项类型声明 * 字典分页项类型声明
*/ */
export interface DictItemPageResult extends PageResult<DictItem[]> { export type DictItemPageResult = PageResult<DictItem[]>
}
/** /**
* 字典表单类型声明 * 字典表单类型声明
*/ */
export interface DictItemFormData { export interface DictItemFormData {
id: number | undefined; id?: number;
dictCode:string, dictCode?:string,
dictName?:string;
name: string; name: string;
code: string; code: string;
value: string; value: string;

View File

@@ -2,8 +2,8 @@
/** /**
* *
*/ */
export interface MenuQueryParam { export interface MenuQueryParam {
name: String | undefined name?: string
} }
/** /**

View File

@@ -23,9 +23,7 @@ export interface PermItem {
/** /**
* 权限分页项类型声明 * 权限分页项类型声明
*/ */
export interface PermPageResult extends PageResult<PermItem[]> { export type PermPageResult = PageResult<PermItem[]>
}
/** /**
* 权限表单类型声明 * 权限表单类型声明

View File

@@ -4,7 +4,7 @@ import { PageQueryParam, PageResult } from "../base"
* 角色查询参数类型声明 * 角色查询参数类型声明
*/ */
export interface RoleQueryParam extends PageQueryParam { export interface RoleQueryParam extends PageQueryParam {
name: String | undefined name?: string
} }
/** /**
@@ -25,9 +25,7 @@ export interface RoleItem {
/** /**
* 角色分页项类型声明 * 角色分页项类型声明
*/ */
export interface RolePageResult extends PageResult<RoleItem[]> { export type RolePageResult = PageResult<RoleItem[]>
}
/** /**
* 角色表单类型声明 * 角色表单类型声明

View File

@@ -14,9 +14,9 @@ export interface UserInfo {
* 用户查询参数类型声明 * 用户查询参数类型声明
*/ */
export interface UserQueryParam extends PageQueryParam { export interface UserQueryParam extends PageQueryParam {
keywords: String | undefined, keywords: string,
status: number | undefined, status: number,
deptId: number | undefined deptId: number
} }
/** /**
@@ -39,9 +39,7 @@ export interface UserItem {
/** /**
* 用户分页项类型声明 * 用户分页项类型声明
*/ */
export interface UserPageResult extends PageResult<UserItem[]> { export type UserPageResult = PageResult<UserItem[]>
}
/** /**
* 用户表单类型声明 * 用户表单类型声明
@@ -60,11 +58,10 @@ export interface UserFormData {
roleIds: number[] roleIds: number[]
} }
/** /**
* 用户导入表单类型声明 * 用户导入表单类型声明
*/ */
export interface UserImportFormData { export interface UserImportFormData {
deptId: number, deptId: number,
roleIds: number[] roleIds: number[]
} }

View File

@@ -4,7 +4,7 @@ import { PageQueryParam, PageResult } from "../base"
* 会员查询参数类型声明 * 会员查询参数类型声明
*/ */
export interface MemberQueryParam extends PageQueryParam { export interface MemberQueryParam extends PageQueryParam {
nickName: String | undefined nickName?: string
} }
/** /**
@@ -46,9 +46,7 @@ export interface AddressItem {
/** /**
* 会员分页项类型声明 * 会员分页项类型声明
*/ */
export interface MemberPageResult extends PageResult<MemberItem[]> { export type MemberPageResult = PageResult<MemberItem[]>
}
/** /**
* 会员表单类型声明 * 会员表单类型声明

View File

@@ -31,17 +31,17 @@ export function removeClass(ele: HTMLElement, cls: string) {
export function mix(color1: string, color2: string, weight: number) { export function mix(color1: string, color2: string, weight: number) {
weight = Math.max(Math.min(Number(weight), 1), 0); weight = Math.max(Math.min(Number(weight), 1), 0);
let r1 = parseInt(color1.substring(1, 3), 16); const r1 = parseInt(color1.substring(1, 3), 16);
let g1 = parseInt(color1.substring(3, 5), 16); const g1 = parseInt(color1.substring(3, 5), 16);
let b1 = parseInt(color1.substring(5, 7), 16); const b1 = parseInt(color1.substring(5, 7), 16);
let r2 = parseInt(color2.substring(1, 3), 16); const r2 = parseInt(color2.substring(1, 3), 16);
let g2 = parseInt(color2.substring(3, 5), 16); const g2 = parseInt(color2.substring(3, 5), 16);
let b2 = parseInt(color2.substring(5, 7), 16); const b2 = parseInt(color2.substring(5, 7), 16);
let r = Math.round(r1 * (1 - weight) + r2 * weight); const r = Math.round(r1 * (1 - weight) + r2 * weight);
let g = Math.round(g1 * (1 - weight) + g2 * weight); const g = Math.round(g1 * (1 - weight) + g2 * weight);
let b = Math.round(b1 * (1 - weight) + b2 * weight); const b = Math.round(b1 * (1 - weight) + b2 * weight);
const rStr = ("0" + (r || 0).toString(16)).slice(-2); const rStr = ("0" + (r || 0).toString(16)).slice(-2);
const gStr = ("0" + (g || 0).toString(16)).slice(-2); const gStr = ("0" + (g || 0).toString(16)).slice(-2);
const bStr = ("0" + (b || 0).toString(16)).slice(-2); const bStr = ("0" + (b || 0).toString(16)).slice(-2);
return "#" + rStr + gStr + bStr; return "#" + rStr + gStr + bStr;
}; }

View File

@@ -52,10 +52,6 @@ service.interceptors.response.use(
localStorage.clear(); // 清除浏览器全部缓存 localStorage.clear(); // 清除浏览器全部缓存
window.location.href = '/'; // 跳转登录页 window.location.href = '/'; // 跳转登录页
ElMessageBox.alert('当前页面已失效,请重新登录', '提示', {}) ElMessageBox.alert('当前页面已失效,请重新登录', '提示', {})
.then(() => {
})
.catch(() => {
});
} else { } else {
ElMessage({ ElMessage({
message: msg || '系统出错', message: msg || '系统出错',

View File

@@ -34,7 +34,7 @@ const position = () => {
* @param {number} duration * @param {number} duration
* @param {Function} callback * @param {Function} callback
*/ */
export const scrollTo = (to: number, duration: number, callback?: Function) => { export const scrollTo = (to: number, duration: number, callback?: any) => {
const start = position() const start = position()
const change = to - start const change = to - start
const increment = 20 const increment = 20

View File

@@ -8,7 +8,7 @@ export const localStorage = {
}, },
// 获取永久缓存 // 获取永久缓存
get(key: string) { get(key: string) {
let json: any = window.localStorage.getItem(key); const json: any = window.localStorage.getItem(key);
return JSON.parse(json); return JSON.parse(json);
}, },
// 移除永久缓存 // 移除永久缓存
@@ -31,7 +31,7 @@ export const sessionStorage = {
}, },
// 获取临时缓存 // 获取临时缓存
get(key: string) { get(key: string) {
let json: any = window.sessionStorage.getItem(key); const json: any = window.sessionStorage.getItem(key);
return JSON.parse(json); return JSON.parse(json);
}, },
// 移除临时缓存 // 移除临时缓存

View File

@@ -11,7 +11,6 @@
import {nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted} from "vue"; import {nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted} from "vue";
import {init, EChartsOption} from 'echarts' import {init, EChartsOption} from 'echarts'
import resize from "@/utils/resize"; import resize from "@/utils/resize";
import * as echarts from "echarts";
const props = defineProps({ const props = defineProps({
id: { id: {

View File

@@ -7,7 +7,7 @@
</template> </template>
<el-tabs v-model="teamActiveName"> <el-tabs v-model="teamActiveName">
<el-tab-pane label="开发者「无回」" name="developer"> <el-tab-pane label="开发者「无回」" name="developer">
<div class="developer" ref="dev_wapper"> <div class="developer" ref="dev_wrapper">
<ul class="developer__container"> <ul class="developer__container">
<li class="developer__item" v-for="(item, index) in developers" :key="index"> <li class="developer__item" v-for="(item, index) in developers" :key="index">
<div class="developer__inner"> <div class="developer__inner">
@@ -21,9 +21,10 @@
<div class="developer__position"> <div class="developer__position">
<el-tag <el-tag
v-for="(position, i) in item.positions" v-for="(position, i) in item.positions"
:type="colors[i % colors.length]" :type="(colors[i % colors.length] as any)"
:class="i !== 0 ? 'f-ml' : ''" :class="i !== 0 ? 'f-ml' : ''"
size="small" size="small"
:key="i"
>{{ position }}</el-tag> >{{ position }}</el-tag>
</div> </div>
<div class="developer__homepage"> <div class="developer__homepage">
@@ -41,8 +42,8 @@
<div class="group"> <div class="group">
<el-image <el-image
class="group-img" class="group-img"
src="https://cdn.youlai.tech/youlaiqun.png" src="https://www.youlai.tech/files/blog/youlaiqun.png"
:preview-src-list="['https://cdn.youlai.tech/youlaiqun.png']" :preview-src-list="['https://www.youlai.tech/files/blog/youlaiqun.png']"
/> />
<div class="group-tip">群二维码过期可添加开发者微信由其拉入群备注有来即可</div> <div class="group-tip">群二维码过期可添加开发者微信由其拉入群备注有来即可</div>
</div> </div>
@@ -99,10 +100,10 @@ const { teamActiveName, developers, colors, indicatorImgUrl } = toRefs(state);
let bScroll = reactive({}) let bScroll = reactive({})
const dev_wapper = ref<HTMLElement | any>(null) const dev_wrapper = ref<HTMLElement | any>(null)
onMounted(() => { onMounted(() => {
bScroll = new BScroll(dev_wapper.value, { bScroll = new BScroll(dev_wrapper.value, {
mouseWheel: true,//开启鼠标滚轮 mouseWheel: true,//开启鼠标滚轮
disableMouse: false,//启用鼠标拖动 disableMouse: false,//启用鼠标拖动
scrollX: true, //X轴滚动启用 scrollX: true, //X轴滚动启用

View File

@@ -86,68 +86,32 @@
<!-- Echarts 图表 --> <!-- Echarts 图表 -->
<el-row :gutter="40" style="margin-top: 20px"> <el-row :gutter="40" style="margin-top: 20px">
<el-col :sm="24" :lg="8" class="card-panel-col"> <el-col :sm="24" :lg="8" class="card-panel-col">
<BarChart <BarChart id="barChart" height="400px" width="100%" class="chart-container" />
id="barChart"
height="400px"
width="100%"
class="chart-container"
/>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :lg="8" class="card-panel-col"> <el-col :xs="24" :sm="12" :lg="8" class="card-panel-col">
<PieChart <PieChart id="pieChart" height="400px" width="100%" class="chart-container" />
id="pieChart"
height="400px"
width="100%"
class="chart-container"
/>
<!--订单漏斗图--> <!--订单漏斗图-->
<!--<FunnelChart id="funnelChart" height="400px" width="100%" class="chart-container"/>--> <!--<FunnelChart id="funnelChart" height="400px" width="100%" class="chart-container"/>-->
</el-col> </el-col>
<el-col :xs="24" :sm="12" :lg="8" class="card-panel-col"> <el-col :xs="24" :sm="12" :lg="8" class="card-panel-col">
<RadarChart <RadarChart id="radarChart" height="400px" width="100%" class="chart-container" />
id="radarChart"
height="400px"
width="100%"
class="chart-container"
/>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
// Vue引用
import {
computed,
nextTick,
onMounted,
reactive,
toRefs,
watchEffect,
} from "vue";
// 组件引用 // 组件引用
import GithubCorner from "@/components/GithubCorner/index.vue"; import GithubCorner from "@/components/GithubCorner/index.vue";
import SvgIcon from "@/components/SvgIcon/index.vue"; import SvgIcon from "@/components/SvgIcon/index.vue";
import BarChart from "./components/Chart/BarChart.vue"; import BarChart from "./components/Chart/BarChart.vue";
import PieChart from "./components/Chart/PieChart.vue"; import PieChart from "./components/Chart/PieChart.vue";
import RadarChart from "./components/Chart/RadarChart.vue"; import RadarChart from "./components/Chart/RadarChart.vue";
import FunnelChart from "./components/Chart/FunnelChart.vue";
import Project from "./components/Project/index.vue"; import Project from "./components/Project/index.vue";
import Team from "./components/Team/index.vue"; import Team from "./components/Team/index.vue";
import BScroll from "better-scroll";
import useStore from "@/store";
const { user } = useStore();
const roles = computed(() => user.roles);
const avatar = computed(() => user.avatar);
const nickname = computed(() => user.nickname);
</script> </script>
@@ -171,8 +135,7 @@ const nickname = computed(() => user.nickname);
} }
.user-profile { .user-profile {
.user-name { .user-name {}
}
.box-center { .box-center {
padding-top: 10px; padding-top: 10px;

View File

@@ -142,7 +142,6 @@ const state = reactive({
}, },
loading: false, loading: false,
passwordType: "password", passwordType: "password",
redirect: "",
captchaBase64: "", captchaBase64: "",
// 大写提示禁用 // 大写提示禁用
capslockTooltipDisabled: true, capslockTooltipDisabled: true,
@@ -164,7 +163,6 @@ const {
loginRules, loginRules,
loading, loading,
passwordType, passwordType,
redirect,
captchaBase64, captchaBase64,
capslockTooltipDisabled, capslockTooltipDisabled,
showCopyright, showCopyright,

View File

@@ -118,10 +118,10 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue"; import { onMounted, reactive, ref, toRefs } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus"; import { ElForm } from "element-plus";
import { Dialog, Order, OrderQueryParam } from "@/types"; import { Dialog, Order, OrderQueryParam } from "@/types";
import { listOrderPages, getOrderDetail } from "@/api/oms/order"; import { listOrderPages, getOrderDetail } from "@/api/oms/order";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue"; import { Search, Refresh } from "@element-plus/icons-vue";
const queryFormRef = ref(ElForm); const queryFormRef = ref(ElForm);
@@ -196,14 +196,10 @@ const state = reactive({
const { const {
loading, loading,
single,
multiple,
queryParams, queryParams,
orderList, orderList,
total, total,
dialog,
dateRange, dateRange,
orderDetail,
} = toRefs(state); } = toRefs(state);
function handleQuery() { function handleQuery() {
@@ -227,9 +223,6 @@ function viewDetail(row: any) {
}); });
} }
function cancel() {
state.dialog.visible = false;
}
onMounted(() => { onMounted(() => {
handleQuery(); handleQuery();

View File

@@ -3,16 +3,8 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" <el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
>新增</el-button <el-button type="danger" :icon="Delete" click="handleDelete" :disabled="multiple">删除</el-button>
>
<el-button
type="danger"
:icon="Delete"
click="handleDelete"
:disabled="multiple"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item prop="name"> <el-form-item prop="name">
@@ -20,20 +12,13 @@
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
>搜索</el-button <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
>
<el-button :icon="Refresh" @click="resetForm">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table <el-table v-loading="loading" :data="brandList" @selection-change="handleSelectionChange" border>
v-loading="loading"
:data="brandList"
@selection-change="handleSelectionChange"
border
>
<el-table-column type="selection" min-width="5%" /> <el-table-column type="selection" min-width="5%" />
<el-table-column prop="name" label="品牌名称" min-width="10" /> <el-table-column prop="name" label="品牌名称" min-width="10" />
<el-table-column prop="logoUrl" label="LOGO" min-width="10"> <el-table-column prop="logoUrl" label="LOGO" min-width="10">
@@ -41,10 +26,7 @@
<el-popover placement="right" :width="400" trigger="hover"> <el-popover placement="right" :width="400" trigger="hover">
<img :src="scope.row.logoUrl" width="400" height="400" /> <img :src="scope.row.logoUrl" width="400" height="400" />
<template #reference> <template #reference>
<img <img :src="scope.row.logoUrl" style="max-height: 60px; max-width: 60px" />
:src="scope.row.logoUrl"
style="max-height: 60px; max-width: 60px"
/>
</template> </template>
</el-popover> </el-popover>
</template> </template>
@@ -54,46 +36,19 @@
<el-table-column label="操作" width="150"> <el-table-column label="操作" width="150">
<template #default="scope"> <template #default="scope">
<el-button <el-button @click="handleUpdate(scope.row)" type="primary" :icon="Edit" circle plain />
@click="handleUpdate(scope.row)" <el-button type="danger" :icon="Delete" circle plain @click="handleDelete(scope.row)" />
type="primary"
:icon="Edit"
circle
plain
/>
<el-button
type="danger"
:icon="Delete"
circle
plain
@click="handleDelete(scope.row)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
v-show="total > 0" @pagination="handleQuery" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<!-- 表单弹窗 --> <!-- 表单弹窗 -->
<el-dialog <el-dialog :title="dialog.title" v-model="dialog.visible" top="5vh" width="600px">
:title="dialog.title" <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
v-model="dialog.visible"
top="5vh"
width="600px"
>
<el-form
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="100px"
>
<el-form-item label="品牌名称" prop="name"> <el-form-item label="品牌名称" prop="name">
<el-input v-model="formData.name" auto-complete="off" /> <el-input v-model="formData.name" auto-complete="off" />
</el-form-item> </el-form-item>
@@ -118,7 +73,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs, unref } from "vue"; import { onMounted, reactive, ref, toRefs } from "vue";
import { ElForm, ElTable, ElMessage, ElMessageBox } from "element-plus"; import { ElForm, ElTable, ElMessage, ElMessageBox } from "element-plus";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue"; import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { BrandFormData, BrandItem, BrandQueryParam, Dialog } from "@/types"; import { BrandFormData, BrandItem, BrandQueryParam, Dialog } from "@/types";
@@ -156,14 +111,13 @@ const state = reactive({
required: true, required: true,
message: "请输入品牌名称", message: "请输入品牌名称",
trigger: "blur", trigger: "blur",
}, }
], ]
}, },
}); });
const { const {
loading, loading,
single,
multiple, multiple,
queryParams, queryParams,
brandList, brandList,
@@ -211,17 +165,20 @@ function handleUpdate(row: any) {
}); });
} }
/**
* 表单提交
*/
function submitForm() { function submitForm() {
dataFormRef.value.validate((isValid: boolean) => { dataFormRef.value.validate((isValid: boolean) => {
if (isValid) { if (isValid) {
if (state.formData.id) { if (state.formData.id) {
updateBrand(state.formData.id, state.formData).then((response) => { updateBrand(state.formData.id, state.formData).then(() => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
cancel(); cancel();
handleQuery(); handleQuery();
}); });
} else { } else {
addBrand(state.formData).then((response) => { addBrand(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
cancel(); cancel();
handleQuery(); handleQuery();
@@ -232,18 +189,16 @@ function submitForm() {
} }
/** /**
* 重置表单 * 取消
*/ */
function resetForm() { function cancel() {
state.formData.id = undefined; state.dialog.visible = false;
dataFormRef.value.resetFields(); dataFormRef.value.resetFields();
} }
function cancel() { /**
state.dialog.visible = false; * 删除
resetForm(); */
}
function handleDelete(row: any) { function handleDelete(row: any) {
const ids = [row.id || state.ids].join(","); const ids = [row.id || state.ids].join(",");
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", { ElMessageBox.confirm("确认删除已选中的数据项?", "警告", {

View File

@@ -7,43 +7,21 @@
<el-tag v-else type="info"><i class="el-icon-info"></i> 请选择商品分类</el-tag> <el-tag v-else type="info"><i class="el-icon-info"></i> 请选择商品分类</el-tag>
</el-col> </el-col>
<el-col :span="12" style="text-align: right"> <el-col :span="12" style="text-align: right">
<el-button type="primary" :icon="Check" @click="submitForm">提交</el-button> <el-button type="primary" :icon="Check" @click="submitForm">提交</el-button>
</el-col> </el-col>
</el-row> </el-row>
<el-row style="margin-top: 10px"> <el-row style="margin-top: 10px">
<el-form <el-form ref="form" :model="formData" :disabled="category?.childrenLen > 0" label-width="100">
ref="form" <el-form-item v-for="(item, index) in formData.attributes" :key="index" :label="attributeTypeName + (index + 1)"
:model="formData" :prop="'attributes.' + index + '.name'" :rules="rules.attribute.name">
:disabled="category?.childrenLen>0" <el-input v-model="item.name" style="width: 300px" />
label-width="100"
>
<el-form-item
v-for="(item, index) in formData.attributes"
:label="attributeTypeName + (index+1)"
:prop="'attributes.' + index + '.name'"
:rules="rules.attribute.name"
>
<el-input v-model="item.name" style="width: 300px"/>
<el-button <el-button v-if="index === 0" type="success" :icon="Plus" circle plain @click.prevent="handleAdd()"
v-if="index===0" style="margin-left: 15px" />
type="success"
:icon="Plus"
circle
plain
@click.prevent="handleAdd()"
style="margin-left: 15px"
/>
<el-button <el-button type="danger" :icon="Delete" plain circle @click.prevent="handleDelete(index)"
type="danger" style="margin-left: 15px" />
:icon="Delete"
plain
circle
@click.prevent="handleDelete(index)"
style="margin-left: 15px"
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-row> </el-row>
@@ -52,11 +30,12 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {listAttributes, saveAttributeBatch} from "@/api/pms/attribute";
import {computed, reactive, toRefs, watch} from "vue";
import {Plus, Check, Delete} from '@element-plus/icons-vue' import { computed, reactive, toRefs, watch } from "vue";
import {ElMessage} from "element-plus"; import { listAttributes, saveAttributeBatch } from "@/api/pms/attribute";
import SvgIcon from '@/components/SvgIcon/index.vue'; import { Plus, Check, Delete } from '@element-plus/icons-vue'
import { ElMessage } from "element-plus";
const props = defineProps({ const props = defineProps({
attributeType: { attributeType: {
@@ -65,10 +44,12 @@ const props = defineProps({
}, },
category: { category: {
type: Object, type: Object,
default: { default: () => {
id: undefined, return {
name: '', id: undefined,
childrenLen: 0 name: '',
childrenLen: 0
}
} }
} }
}) })
@@ -95,20 +76,20 @@ const state = reactive({
rules: { rules: {
attribute: { attribute: {
name: [ name: [
{required: true, validator: attributeNameValidator, trigger: 'blur'} { required: true, validator: attributeNameValidator, trigger: 'blur' }
] ]
} }
} }
}) })
const {formData, rules} = toRefs(state) const { formData, rules } = toRefs(state)
watch(() => props.category.id as any, (newVal, oldVal) => { watch(() => props.category.id as any, () => {
const categoryId = props.category.id const categoryId = props.category.id
if (categoryId) { if (categoryId) {
listAttributes({categoryId: categoryId,type:props.attributeType}).then(response => { listAttributes({ categoryId: categoryId, type: props.attributeType }).then(response => {
const {data} = response const { data } = response
if (data && data.length > 0) { if (data && data.length > 0) {
state.formData.attributes = response.data state.formData.attributes = response.data
} else { } else {
@@ -156,8 +137,7 @@ function submitForm() {
</script> </script>
<style scoped> <style scoped>
.component-container{ .component-container {
margin-bottom: 20px; margin-bottom: 20px;
} }
</style> </style>

View File

@@ -1,83 +1,47 @@
<!-- 商品分类层级最多为三层level字段标识 --> <!-- 商品分类层级最多为三层level字段标识 -->
<template> <template>
<div class="component-container"> <div class="component-container">
<el-tree v-loading="loading" ref="categoryTreeRef" :data="categoryOptions"
<el-tree :props="{ label: 'name', children: 'children', disabled: '' }" node-key="id" :expand-on-click-node="false"
v-loading="loading" default-expand-all :accordion="true" @node-click="handleNodeClick">
ref="categoryTreeRef"
:data="categoryOptions"
:props="{ label: 'name', children: 'children',disabled:'' }"
node-key="id"
:expand-on-click-node="false"
default-expand-all
:accordion="true"
@node-click="handleNodeClick"
>
<template #default="scope"> <template #default="scope">
<div class="custom-tree-node"> <div class="custom-tree-node">
<span> <span>
<el-image <el-image v-show="scope.data.level == 3" :src="scope.data.iconUrl"
v-show="scope.data.level == 3 " style="width: 20px; height:20px; vertical-align: middle;margin-top: -5px">
:src="scope.data.iconUrl"
style="width: 20px; height:20px; vertical-align: middle;margin-top: -5px"
>
<template #error> <template #error>
<div class="image-slot"> <div class="image-slot">
<Picture style="width: 20px; height:20px;"/> <Picture style="width: 20px; height:20px;" />
</div> </div>
</template> </template>
</el-image> </el-image>
{{ scope.data.name }} {{ scope.data.name }}
</span> </span>
<span> <span>
<el-button <el-button v-show="scope.data.level != 3" type="success" :icon="Plus" circle plain
v-show="scope.data.level != 3 " @click.stop="handleAdd(scope.data)" />
type="success" <el-button v-show="scope.data.id !== 0" type="warning" :icon="Edit" circle plain
:icon="Plus" @click.stop="handleUpdate(scope.data)" />
circle <el-button v-show="scope.data.id && (!scope.data.children || scope.data.children.length <= 0)" type="danger"
plain :icon="Delete" circle plain @click.stop="handleDelete(scope.data)" />
@click.stop="handleAdd(scope.data)"/> </span>
<el-button
v-show="scope.data.id !== 0"
type="warning"
:icon="Edit"
circle
plain
@click.stop="handleUpdate(scope.data)"/>
<el-button
v-show="scope.data.id && (!scope.data.children || scope.data.children.length <= 0)"
type="danger"
:icon="Delete"
circle
plain
@click.stop="handleDelete(scope.data)"/>
</span>
</div> </div>
</template> </template>
</el-tree> </el-tree>
<el-dialog <el-dialog :title="dialog.title" v-model="dialog.visible" width="750px">
:title="dialog.title"
v-model="dialog.visible"
width="750px"
>
<el-form <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="100px"
>
<el-form-item label="上级分类" prop="parentId"> <el-form-item label="上级分类" prop="parentId">
<el-input v-model="parent.name" readonly/> <el-input v-model="parent.name" readonly />
</el-form-item> </el-form-item>
<el-form-item label="分类名称" prop="name"> <el-form-item label="分类名称" prop="name">
<el-input v-model="formData.name"/> <el-input v-model="formData.name" />
</el-form-item> </el-form-item>
<el-form-item label="分类图标" prop="iconUrl"> <el-form-item label="分类图标" prop="iconUrl">
<single-upload v-model="formData.iconUrl"/> <single-upload v-model="formData.iconUrl" />
</el-form-item> </el-form-item>
<el-form-item label="显示状态" prop="visible"> <el-form-item label="显示状态" prop="visible">
@@ -103,11 +67,11 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {listCategories, addCategory, updateCategory, deleteCategories} from '@/api/pms/category' import { listCategories, addCategory, updateCategory, deleteCategories } from '@/api/pms/category'
import {Search, Plus, Edit, Refresh, Delete} from '@element-plus/icons-vue' import { Plus, Edit, Delete } from '@element-plus/icons-vue'
import SingleUpload from '@/components/Upload/SingleUpload.vue' import SingleUpload from '@/components/Upload/SingleUpload.vue'
import {getCurrentInstance, onMounted, reactive, ref, toRefs, unref} from "vue"; import { onMounted, reactive, ref, toRefs, unref, defineEmits } from "vue";
import {ElForm, ElMessage, ElMessageBox, ElTree} from "element-plus"; import { ElForm, ElMessage, ElMessageBox, ElTree } from "element-plus";
const emit = defineEmits(['categoryClick']) const emit = defineEmits(['categoryClick'])
@@ -145,7 +109,7 @@ const state = reactive({
current: {} as any current: {} as any
}) })
const {loading, categoryOptions, formData, rules, dialog, parent} = toRefs(state) const { loading, categoryOptions, formData, rules, dialog, parent } = toRefs(state)
function handleQuery() { function handleQuery() {
state.loading = true state.loading = true
@@ -159,7 +123,6 @@ function handleQuery() {
}] }]
state.loading = false state.loading = false
}) })
} }
function handleNodeClick(row: any) { function handleNodeClick(row: any) {
@@ -175,9 +138,7 @@ function handleNodeClick(row: any) {
emit('categoryClick', row) emit('categoryClick', row)
} }
function handleAdd(row: any) { function handleAdd(row: any) {
resetForm()
state.dialog = { state.dialog = {
title: '新增商品分类', title: '新增商品分类',
visible: true visible: true
@@ -192,7 +153,6 @@ function handleAdd(row: any) {
} }
function handleUpdate(row: any) { function handleUpdate(row: any) {
resetForm()
handleNodeClick(row) handleNodeClick(row)
state.dialog = { state.dialog = {
title: '修改商品分类', title: '修改商品分类',
@@ -202,24 +162,23 @@ function handleUpdate(row: any) {
} }
function submitForm() { function submitForm() {
const form = unref(dataFormRef) dataFormRef.value.validate((valid: any) => {
form.validate((valid: any) => {
if (valid) { if (valid) {
if (state.formData.id) { if (state.formData.id) {
updateCategory(state.formData.id, state.formData).then(response => { updateCategory(state.formData.id, state.formData).then(() => {
ElMessage.success('修改成功') ElMessage.success('修改成功')
state.dialog.visible = false cancel()
handleQuery() handleQuery()
}) })
} else { } else {
const parentCategory= state.parent as any const parentCategory = state.parent as any
console.log('parent',parentCategory) console.log('parent', parentCategory)
state.formData.parentId = parentCategory.id state.formData.parentId = parentCategory.id
state.formData.level = parentCategory.level+1 state.formData.level = parentCategory.level + 1
addCategory(state.formData).then(() => { addCategory(state.formData).then(() => {
ElMessage.success('新增成功') ElMessage.success('新增成功')
state.dialog.visible = false cancel()
handleQuery() handleQuery()
}) })
} }
@@ -241,20 +200,9 @@ function handleDelete(row: any) {
}) })
} }
function resetForm() {
state.formData = {
id: undefined,
name: undefined,
parentId: 0,
level: undefined,
iconUrl: undefined,
visible: 1,
sort: 100
}
}
function cancel() { function cancel() {
resetForm() state.dialog.visible = false;
dataFormRef.value.resetFields();
state.dialog.visible = false state.dialog.visible = false
} }

View File

@@ -7,7 +7,7 @@
<svg-icon icon-class="menu"/> <svg-icon icon-class="menu"/>
商品分类 商品分类
</template> </template>
<category ref="categoryRef" @categoryClick="handleCategoryClick"/> <Category ref="categoryRef" @categoryClick="handleCategoryClick"/>
</el-card> </el-card>
</el-col> </el-col>
@@ -18,9 +18,9 @@
{{category.name}} 规格属性 {{category.name}} 规格属性
</template> </template>
<!-- 商品规格 --> <!-- 商品规格 -->
<attribute ref="specificationRef" :attributeType="1" :category="category"/> <Attribute ref="specificationRef" :attributeType="1" :category="category"/>
<!-- 商品属性 --> <!-- 商品属性 -->
<attribute ref="attributeRef" :attributeType="2" :category="category"/> <Attribute ref="attributeRef" :attributeType="2" :category="category"/>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>

View File

@@ -4,35 +4,15 @@
<el-card class="box-card"> <el-card class="box-card">
<template #header> <template #header>
<span>商品属性</span> <span>商品属性</span>
<el-button <el-button style="float: right" type="success" :icon="Plus" size="small" @click="handleAdd">
style="float: right"
type="success"
:icon="Plus"
size="small"
@click="handleAdd"
>
添加属性 添加属性
</el-button> </el-button>
</template> </template>
<el-form <el-form ref="dataFormRef" :model="goodsInfo" :rules="rules" size="small" :inline="true">
ref="dataForm" <el-table :data="goodsInfo.attrList" size="small" highlight-current-row border>
:model="modelValue"
:rules="rules"
size="small"
:inline="true"
>
<el-table
:data="modelValue.attrList"
size="small"
highlight-current-row
border
>
<el-table-column property="name" label="属性名称"> <el-table-column property="name" label="属性名称">
<template #default="scope"> <template #default="scope">
<el-form-item <el-form-item :prop="'attrList[' + scope.$index + '].name'" :rules="rules.name">
:prop="'attrList[' + scope.$index + '].name'"
:rules="rules.name"
>
<el-input v-model="scope.row.name" /> <el-input v-model="scope.row.name" />
</el-form-item> </el-form-item>
</template> </template>
@@ -40,10 +20,7 @@
<el-table-column property="value" label="属性值"> <el-table-column property="value" label="属性值">
<template #default="scope"> <template #default="scope">
<el-form-item <el-form-item :prop="'attrList[' + scope.$index + '].value'" :rules="rules.value">
:prop="'attrList[' + scope.$index + '].value'"
:rules="rules.value"
>
<el-input v-model="scope.row.value" /> <el-input v-model="scope.row.value" />
</el-form-item> </el-form-item>
</template> </template>
@@ -52,15 +29,8 @@
<el-table-column label="操作" width="150"> <el-table-column label="操作" width="150">
<template #default="scope"> <template #default="scope">
<el-form-item> <el-form-item>
<el-button <el-button v-if="scope.$index > 0" type="danger" :icon="Minus" size="small" circle plain
v-if="scope.$index > 0" @click.stop="handleRemove(scope.$index)" />
type="danger"
:icon="Minus"
size="small"
circle
plain
@click.stop="handleRemove(scope.$index)"
/>
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
@@ -70,60 +40,60 @@
</div> </div>
<div class="component-container__footer"> <div class="component-container__footer">
<el-button @click="handlePrev">上一步填写商品信息</el-button> <el-button @click="handlePrev">上一步填写商品信息</el-button>
<el-button type="primary" @click="handleNext" <el-button type="primary" @click="handleNext">下一步设置商品库存</el-button>
>下一步设置商品库存</el-button
>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, reactive, ref, toRefs, watch } from "vue";
import { listAttributes } from "@/api/pms/attribute"; import { listAttributes } from "@/api/pms/attribute";
import { computed, nextTick, reactive, ref, toRefs, unref, watch } from "vue";
import { ElForm } from "element-plus"; import { ElForm } from "element-plus";
import { Plus, Minus } from "@element-plus/icons-vue"; import { Plus, Minus } from "@element-plus/icons-vue";
const emit = defineEmits(["prev", "next"]); const emit = defineEmits(["prev", "next", "update:modelValue"]);
const dataForm = ref(ElForm); const dataFormRef = ref(ElForm);
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default: {}, default: () => { }
}, }
}); });
const categoryId = computed(() => props.modelValue.categoryId); const goodsInfo: any = computed({
get: () => props.modelValue,
set: (value) => {
emit('update:modelValue', value)
}
})
watch( watch(() => goodsInfo.value.categoryId, (newVal) => {
categoryId, // 商品编辑不加载分类下的属性
(newVal) => { const goodsId = goodsInfo.value.id;
// 商品编辑不加载分类下的属性 if (goodsId) {
const spuId = props.modelValue.id; return false;
if (spuId) { }
return false; // 商品新增加载默认分类下的属性
} if (newVal) {
// type=2 商品分类下的属性
listAttributes({ categoryId: newVal, type: 2 }).then((response) => {
const attrList = response.data;
if (attrList && attrList.length > 0) {
goodsInfo.value.attrList = attrList;
} else {
goodsInfo.value.attrList = [{}];
}
});
} else {
goodsInfo.value.attrList = [{}];
}
// 商品新增加载默认分类下的属性 },
if (newVal) {
// type=2 商品分类下的属性
listAttributes({ categoryId: newVal, type: 2 }).then((response) => {
const attrList = response.data;
if (attrList && attrList.length > 0) {
props.modelValue.attrList = attrList;
} else {
props.modelValue.attrList = [{}];
}
});
} else {
props.modelValue.attrList = [{}];
}
},
{ {
immediate: true, immediate: true,
deep: true, deep: true
} })
);
const state = reactive({ const state = reactive({
rules: { rules: {
@@ -135,11 +105,11 @@ const state = reactive({
const { rules } = toRefs(state); const { rules } = toRefs(state);
function handleAdd() { function handleAdd() {
props.modelValue.attrList.push({}); goodsInfo.value.attrList.push({});
} }
function handleRemove(index: number) { function handleRemove(index: number) {
props.modelValue.attrList.splice(index, 1); goodsInfo.value.attrList.splice(index, 1);
} }
function handlePrev() { function handlePrev() {
@@ -147,8 +117,7 @@ function handlePrev() {
} }
function handleNext() { function handleNext() {
const form = unref(dataForm); dataFormRef.value.validate((valid: any) => {
form.validate((valid: any) => {
if (valid) { if (valid) {
emit("next"); emit("next");
} }

View File

@@ -1,20 +1,14 @@
<template> <template>
<div class="component-container"> <div class="component-container">
<div class="component-container__main"> <div class="component-container__main">
<el-cascader-panel <el-cascader-panel ref="categoryRef" :options="categoryOptions" v-model="goodsInfo.categoryId"
ref="categoryRef" :props="{ emitPath: false }" @change="handleCategoryChange" />
:options="categoryOptions"
v-model="modelValue.categoryId"
:props="{emitPath:false}"
@change="handleCategoryChange"
/>
<div style="margin-top: 20px"> <div style="margin-top: 20px">
<el-link type="info" :underline="false" v-show="pathLabels.length>0">您选择的商品分类:</el-link> <el-link type="info" :underline="false" v-show="pathLabels.length > 0">您选择的商品分类:</el-link>
<el-link type="danger" :underline="false" v-for="(item,index) in pathLabels" style="margin-left: 5px"> <el-link type="danger" :underline="false" v-for="(item, index) in pathLabels" :key="index"
style="margin-left: 5px">
{{ item }} {{ item }}
<CaretRight v-show="index<pathLabels.length-1" style="width: 1em; height:1em;margin-left: 5px"/> <CaretRight v-show="index < pathLabels.length - 1" style="width: 1em; height:1em;margin-left: 5px" />
</el-link> </el-link>
</div> </div>
@@ -26,18 +20,27 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {onMounted,nextTick, reactive, ref, toRefs} from "vue"; import { onMounted, nextTick, reactive, ref, toRefs } from "vue";
import {ElCascaderPanel, ElMessage} from "element-plus"; import { ElCascaderPanel, ElMessage } from "element-plus";
import {CaretRight} from '@element-plus/icons-vue'; import { CaretRight } from '@element-plus/icons-vue';
// API 引用 // API 引用
import {listCascadeCategories} from "@/api/pms/category"; import { listCascadeCategories } from "@/api/pms/category";
import { computed } from "@vue/reactivity";
import { GoodsDetail } from "@/types";
const emit = defineEmits(['next']) const emit = defineEmits(['next', "update:modelValue"])
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default:{ } default: () => { }
}
})
const goodsInfo: any = computed({
get: () => props.modelValue,
set: (value) => {
emit('update:modelValue', value)
} }
}) })
@@ -46,12 +49,12 @@ const state = reactive({
pathLabels: [] pathLabels: []
}) })
const {categoryOptions, pathLabels} = toRefs(state) const { categoryOptions, pathLabels } = toRefs(state)
function loadData() { function loadData() {
listCascadeCategories({}).then(response => { listCascadeCategories().then(response => {
state.categoryOptions = response.data state.categoryOptions = response.data
if (props.modelValue.id) { if (goodsInfo.value.id) {
nextTick(() => { nextTick(() => {
handleCategoryChange() handleCategoryChange()
}) })
@@ -63,15 +66,15 @@ const categoryRef = ref(ElCascaderPanel)
function handleCategoryChange() { function handleCategoryChange() {
const checkNode = categoryRef.value.getCheckedNodes()[0] const checkNode = categoryRef.value.getCheckedNodes()[0]
state.pathLabels = checkNode.pathLabels // 商品分类选择层级提示 state.pathLabels = checkNode.pathLabels // 商品分类选择层级提示
props.modelValue.categoryId = checkNode.value goodsInfo.value.categoryId = checkNode.value
} }
function handleNext() { function handleNext() {
if (!props.modelValue.categoryId) { if (!goodsInfo.value.categoryId) {
ElMessage.warning('请选择商品分类') ElMessage.warning('请选择商品分类')
return false return false
} }
emit('next' ) emit('next')
} }
onMounted(() => { onMounted(() => {
@@ -81,7 +84,6 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.component-container { .component-container {
&__main { &__main {
margin: 20px auto margin: 20px auto

View File

@@ -1,74 +1,41 @@
<template> <template>
<div class="component-container"> <div class="component-container">
<div class="component-container__main"> <div class="component-container__main">
<el-form <el-form ref="dataFormRef" :rules="rules" :model="goodsInfo" label-width="120px">
ref="dataFormRef"
:rules="rules"
:model="modelValue"
label-width="120px"
>
<el-form-item label="商品品牌" prop="brandId"> <el-form-item label="商品品牌" prop="brandId">
<el-select <el-select v-model="goodsInfo.brandId" style="width: 400px" clearable>
v-model="modelValue.brandId" <el-option v-for="item in brandOptions" :key="item.id" :label="item.name" :value="item.id" />
style="width: 400px"
clearable
>
<el-option
v-for="item in brandOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="商品名称" prop="name"> <el-form-item label="商品名称" prop="name">
<el-input style="width: 400px" v-model="modelValue.name" /> <el-input style="width: 400px" v-model="goodsInfo.name" />
</el-form-item> </el-form-item>
<el-form-item label="原价" prop="originPrice"> <el-form-item label="原价" prop="originPrice">
<el-input style="width: 400px" v-model="modelValue.originPrice" /> <el-input style="width: 400px" v-model="goodsInfo.originPrice" />
</el-form-item> </el-form-item>
<el-form-item label="现价" prop="price"> <el-form-item label="现价" prop="price">
<el-input style="width: 400px" v-model="modelValue.price" /> <el-input style="width: 400px" v-model="goodsInfo.price" />
</el-form-item> </el-form-item>
<el-form-item label="商品简介"> <el-form-item label="商品简介">
<el-input <el-input type="textarea" :autosize="{ minRows: 3, maxRows: 6 }" v-model="goodsInfo.description" />
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
v-model="modelValue.description"
/>
</el-form-item> </el-form-item>
<el-form-item label="商品相册"> <el-form-item label="商品相册">
<el-card <el-card v-for="(item, index) in pictures" :key="index"
v-for="(item, index) in pictures"
:key="index"
style="width: 170px; display: inline-block; margin-left: 10px;text-align:center" style="width: 170px; display: inline-block; margin-left: 10px;text-align:center"
:body-style="{ padding: '10px' }" :body-style="{ padding: '10px' }">
>
<single-upload v-model="item.url" :show-close="true" /> <single-upload v-model="item.url" :show-close="true" />
<div v-if="item.url"> <div v-if="item.url">
<el-button <el-button type="text" class="button" v-if="item.main == true" style="color: #ff4d51">商品主图</el-button>
type="text" <el-button type="text" class="button" v-else @click="changeMainPicture(index)">设为主图</el-button>
class="button"
v-if="item.main == true"
style="color: #ff4d51"
>商品主图</el-button
>
<el-button
type="text"
class="button"
v-else
@click="changeMainPicture(index)"
>设为主图</el-button
>
</div> </div>
<div v-else> <div v-else>
<!-- 占位 --> <!-- 占位 -->
<el-button type="text" /> <el-button type="text" />
</div> </div>
@@ -76,21 +43,19 @@
</el-form-item> </el-form-item>
<el-form-item label="商品详情" prop="detail"> <el-form-item label="商品详情" prop="detail">
<editor v-model="modelValue.detail" style="height: 600px" /> <editor v-model="goodsInfo.detail" style="height: 600px" />
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
<div class="component-container__footer"> <div class="component-container__footer">
<el-button @click="handlePrev">上一步选择商品分类</el-button> <el-button @click="handlePrev">上一步选择商品分类</el-button>
<el-button type="primary" @click="handleNext" <el-button type="primary" @click="handleNext">下一步设置商品属性</el-button>
>下一步设置商品属性</el-button
>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue"; import { computed, onMounted, reactive, ref, toRefs } from "vue";
import { ElForm } from "element-plus"; import { ElForm } from "element-plus";
// API 依赖 // API 依赖
@@ -100,16 +65,23 @@ import { listBrands } from "@/api/pms/brand";
import Editor from "@/components/WangEditor/index.vue"; import Editor from "@/components/WangEditor/index.vue";
import SingleUpload from "@/components/Upload/SingleUpload.vue"; import SingleUpload from "@/components/Upload/SingleUpload.vue";
const emit = defineEmits(["prev", "next"]); const emit = defineEmits(["prev", "next", "update:modelValue"]);
const dataFormRef = ref(ElForm); const dataFormRef = ref(ElForm);
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default: {}, default: () => { }
}, }
}); });
const goodsInfo: any = computed({
get: () => props.modelValue,
set: (value) => {
emit('update:modelValue', value)
}
})
const state = reactive({ const state = reactive({
brandOptions: [] as Array<any>, brandOptions: [] as Array<any>,
// 商品图册 // 商品图册
@@ -131,17 +103,16 @@ const state = reactive({
const { brandOptions, pictures, rules } = toRefs(state); const { brandOptions, pictures, rules } = toRefs(state);
function loadData() { function loadData() {
listBrands().then(({data}) => { listBrands().then(({ data }) => {
state.brandOptions = data; state.brandOptions = data;
}); });
const goodsInfo = props.modelValue; const goodsId = goodsInfo.value.id;
const goodsId = goodsInfo.id;
if (goodsId) { if (goodsId) {
const mainPicUrl = goodsInfo.picUrl; const mainPicUrl = goodsInfo.value.picUrl;
if (mainPicUrl) { if (mainPicUrl) {
state.pictures.filter((item) => item.main)[0].url = mainPicUrl; state.pictures.filter((item) => item.main)[0].url = mainPicUrl;
} }
const subPicUrls = goodsInfo.subPicUrls; const subPicUrls = goodsInfo.value.subPicUrls;
if (subPicUrls && subPicUrls.length > 0) { if (subPicUrls && subPicUrls.length > 0) {
for (let i = 1; i <= subPicUrls.length; i++) { for (let i = 1; i <= subPicUrls.length; i++) {
state.pictures[i].url = subPicUrls[i - 1]; state.pictures[i].url = subPicUrls[i - 1];
@@ -150,16 +121,6 @@ function loadData() {
} }
} }
function resetForm() {
state.pictures = [
{ url: undefined, main: true }, // main 代表主图,可以切换
{ url: undefined, main: false },
{ url: undefined, main: false },
{ url: undefined, main: false },
{ url: undefined, main: false },
];
}
/** /**
* 切换主图 * 切换主图
*/ */
@@ -185,13 +146,13 @@ function handleNext() {
.filter((item) => item.main == true && item.url) .filter((item) => item.main == true && item.url)
.map((item) => item.url); .map((item) => item.url);
if (mainPicUrl && mainPicUrl.length > 0) { if (mainPicUrl && mainPicUrl.length > 0) {
props.modelValue.picUrl = mainPicUrl[0]; goodsInfo.picUrl = mainPicUrl[0];
} }
const subPicUrl = state.pictures const subPicUrl = state.pictures
.filter((item) => item.main == false && item.url) .filter((item) => item.main == false && item.url)
.map((item) => item.url); .map((item) => item.url);
if (subPicUrl && subPicUrl.length > 0) { if (subPicUrl && subPicUrl.length > 0) {
props.modelValue.subPicUrls = subPicUrl; goodsInfo.subPicUrls = subPicUrl;
} }
emit("next"); emit("next");
} }

View File

@@ -4,29 +4,13 @@
<el-card class="box-card"> <el-card class="box-card">
<template #header> <template #header>
<span>商品规格</span> <span>商品规格</span>
<el-button <el-button :icon="Plus" type="success" @click="handleSpecAdd" size="small" style="float: right">
:icon="Plus"
type="success"
@click="handleSpecAdd"
size="small"
style="float: right"
>
添加规格项 添加规格项
</el-button> </el-button>
</template> </template>
<el-form <el-form ref="specFormRef" :model="specForm" :inline="true" size="small">
ref="specFormRef" <el-table ref="specTableRef" :data="specForm.specList" row-key="id" size="small">
:model="specForm"
:inline="true"
size="small"
>
<el-table
ref="specTableRef"
:data="specForm.specList"
row-key="id"
size="small"
>
<el-table-column align="center" width="50"> <el-table-column align="center" width="50">
<template> <template>
<svg-icon class="drag-handler" icon-class="drag" /> <svg-icon class="drag-handler" icon-class="drag" />
@@ -34,66 +18,31 @@
</el-table-column> </el-table-column>
<el-table-column label="规格名" width="200"> <el-table-column label="规格名" width="200">
<template #default="scope"> <template #default="scope">
<el-form-item <el-form-item :prop="'specList[' + scope.$index + '].name'" :rules="rules.spec.name">
:prop="'specList[' + scope.$index + '].name'" <el-input type="text" v-model="scope.row.name" size="small" @input="handleSpecChange()" />
:rules="rules.spec.name"
>
<el-input
type="text"
v-model="scope.row.name"
size="small"
@input="handleSpecChange()"
/>
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column> <el-table-column>
<template #header> <template #header>
规格值 规格值
<el-link <el-link type="danger" style="font-size: 12px" :underline="false">默认第一条规格包含图片</el-link>
type="danger"
style="font-size: 12px"
:underline="false"
>默认第一条规格包含图片</el-link
>
</template> </template>
<template #default="scope"> <template #default="scope">
<div <div v-for="item in scope.row.values" :key="item.id" style="margin-right: 15px; display: inline-block">
v-for="item in scope.row.values" <el-tag size="small" closable :type="(colors[scope.$index % colors.length] as any)"
:key="item.id" @close="handleSpecValueRemove(scope.$index, item.id)">
style="margin-right: 15px; display: inline-block"
>
<el-tag
size="small"
closable
:type="colors[scope.$index % colors.length]"
@close="handleSpecValueRemove(scope.$index, item.id)"
>
{{ item.value }} {{ item.value }}
</el-tag> </el-tag>
<single-upload <single-upload v-model="item.picUrl" v-if="scope.$index == 0" style="margin-top: 5px" />
v-model="item.picUrl"
v-if="scope.$index == 0"
style="margin-top: 5px"
/>
</div> </div>
<el-input <el-input v-if="tagInputs.length > 0 && tagInputs[scope.$index].visible"
v-if="tagInputs.length > 0 && tagInputs[scope.$index].visible" v-model="tagInputs[scope.$index].value" @keyup.enter="handleSpecValueInput(scope.$index)"
v-model="tagInputs[scope.$index].value" @blur="handleSpecValueInput(scope.$index)" style="width: 80px; vertical-align: top" size="small" />
@keyup.enter="handleSpecValueInput(scope.$index)" <el-button v-else @click="handleSpecValueAdd(scope.$index)" :icon="Plus" style="vertical-align: top"
@blur="handleSpecValueInput(scope.$index)" size="small">
style="width: 80px; vertical-align: top"
size="small"
/>
<el-button
v-else
@click="handleSpecValueAdd(scope.$index)"
:icon="Plus"
style="vertical-align: top"
size="small"
>
添加规格值 添加规格值
</el-button> </el-button>
</template> </template>
@@ -101,14 +50,8 @@
<el-table-column width="60" label="操作"> <el-table-column width="60" label="操作">
<template #default="scope"> <template #default="scope">
<el-button <el-button type="danger" :icon="Minus" size="small" circle plain
type="danger" @click.stop="handleSpecRemove(scope.$index)" />
:icon="Minus"
size="small"
circle
plain
@click.stop="handleSpecRemove(scope.$index)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@@ -120,39 +63,23 @@
<span>商品库存</span> <span>商品库存</span>
</template> </template>
<el-form ref="skuFormRef" :model="skuForm" size="small" :inline="true"> <el-form ref="skuFormRef" :model="skuForm" size="small" :inline="true">
<el-table <el-table :data="skuForm.skuList" :span-method="(objectSpanMethod as any)" highlight-current-row size="small"
:data="skuForm.skuList" border>
:span-method="objectSpanMethod" <el-table-column v-for="(title, index) in specTitles" :key="index" align="center"
highlight-current-row :prop="'specValue' + (index + 1)" :label="title">
size="small"
border
>
<el-table-column
v-for="(title, index) in specTitles"
:key="index"
align="center"
:prop="'specValue' + (index + 1)"
:label="title"
>
</el-table-column> </el-table-column>
<el-table-column label="商品编码" align="center"> <el-table-column label="商品编码" align="center">
<template #default="scope"> <template #default="scope">
<el-form-item <el-form-item :prop="'skuList[' + scope.$index + '].skuSn'" :rules="rules.sku.skuSn">
:prop="'skuList[' + scope.$index + '].skuSn'" <el-input v-model="scope.row.skuSn" />
:rules="rules.sku.skuSn"
>
<el-input v-model="scope.row.skuSn"/>
</el-form-item> </el-form-item>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="价格" align="center"> <el-table-column label="价格" align="center">
<template #default="scope"> <template #default="scope">
<el-form-item <el-form-item :prop="'skuList[' + scope.$index + '].price'" :rules="rules.sku.price">
:prop="'skuList[' + scope.$index + '].price'"
:rules="rules.sku.price"
>
<el-input v-model="scope.row.price" /> <el-input v-model="scope.row.price" />
</el-form-item> </el-form-item>
</template> </template>
@@ -160,10 +87,7 @@
<el-table-column label="库存" align="center"> <el-table-column label="库存" align="center">
<template #default="scope"> <template #default="scope">
<el-form-item <el-form-item :prop="'skuList[' + scope.$index + '].stockNum'" :rules="rules.sku.stockNum">
:prop="'skuList[' + scope.$index + '].stockNum'"
:rules="rules.sku.stockNum"
>
<el-input v-model="scope.row.stockNum" /> <el-input v-model="scope.row.stockNum" />
</el-form-item> </el-form-item>
</template> </template>
@@ -179,17 +103,15 @@
</div> </div>
</template> </template>
<script setup> <script setup lang="ts">
import { import {
computed, computed,
getCurrentInstance,
nextTick, nextTick,
onMounted, onMounted,
reactive, reactive,
ref, ref,
toRefs, toRefs,
unref, watch
watch,
} from "vue"; } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { Plus, Minus } from "@element-plus/icons-vue"; import { Plus, Minus } from "@element-plus/icons-vue";
@@ -204,32 +126,41 @@ import SvgIcon from "@/components/SvgIcon/index.vue";
import SingleUpload from "@/components/Upload/SingleUpload.vue"; import SingleUpload from "@/components/Upload/SingleUpload.vue";
// import Sortable from 'sortablejs' // import Sortable from 'sortablejs'
const categoryId = computed(() => props.modelValue.categoryId);
const emit = defineEmits(["prev", "next"]);
const proxy = getCurrentInstance(); const emit = defineEmits(["prev", "next", 'update:modelValue']);
/* const proxy = getCurrentInstance(); */
const router = useRouter(); const router = useRouter();
const specTableRef = ref(ElTable); /* const specTableRef = ref(ElTable); */
const specFormRef = ref(ElForm); const specFormRef = ref(ElForm);
const skuFormRef = ref(ElForm); const skuFormRef = ref(ElForm);
const props = defineProps({ const props = defineProps({
modelValue: { modelValue: {
type: Object, type: Object,
default: {}, default: () => {
}, return {}
}
}
}); });
const goodsInfo: any = computed({
get: () => props.modelValue,
set: (value) => {
emit('update:modelValue', value)
}
})
const state = reactive({ const state = reactive({
specForm: { specForm: {
specList: [], specList: [] as any[],
}, },
skuForm: { skuForm: {
skuList: [], skuList: [] as any[],
}, },
// 规格项表格标题 // 规格项表格标题
specTitles: [], specTitles: [] as any[],
rules: { rules: {
spec: { spec: {
name: [{ required: true, message: "请输入规格名称", trigger: "blur" }], name: [{ required: true, message: "请输入规格名称", trigger: "blur" }],
@@ -245,56 +176,51 @@ const state = reactive({
}, },
colors: ["", "success", "warning", "danger"], colors: ["", "success", "warning", "danger"],
tagInputs: [{ value: undefined, visible: false }], // 规格值标签临时值和显隐控制 tagInputs: [{ value: undefined, visible: false }], // 规格值标签临时值和显隐控制
loading: undefined,
}); });
const { specForm, skuForm, specTitles, rules, colors, tagInputs, loading } = const { specForm, skuForm, specTitles, rules, colors, tagInputs } =
toRefs(state); toRefs(state);
watch( watch(() => goodsInfo.value.categoryId, (newVal) => {
categoryId, // 商品编辑不加载分类下的规格
(value) => { const goodsId = goodsInfo.value.id;
if (goodsId) {
// 商品编辑不加载分类下的规格 return false;
const spuId = props.modelValue.id; }
if (spuId) { if (newVal) {
return false; // type=1 商品分类下的规格
} listAttributes({ categoryId: newVal, type: 1 }).then((response) => {
if (value) { const specList = response.data;
// type=1 商品分类下的规格 if (specList && specList.length > 0) {
listAttributes({ categoryId: value, type: 1 }).then((response) => { specList.forEach((item: any) => {
const specList = response.data; state.specForm.specList.push({
if (specList && specList.length > 0) { name: item.name,
specList.forEach((item) => { values: [],
state.specForm.specList.push({
name: item.name,
values: [],
});
}); });
loadData(); });
} loadData();
}); }
} });
}, }
},
{ {
immediate: true, immediate: true,
deep: true, deep: true,
} })
);
watch(state.specForm.specList, () => {
watch(state.specForm.specList,(value)=>{
generateSkuList() generateSkuList()
}) })
function loadData() { function loadData() {
props.modelValue.specList.forEach((specItem) => { goodsInfo.value.specList.forEach((specItem: any) => {
const specIndex = state.specForm.specList.findIndex( const specIndex = state.specForm.specList.findIndex(
(item) => item.name == specItem.name (item: any) => item.name == specItem.name
); );
if (specIndex > -1) { if (specIndex > -1) {
state.specForm.specList[specIndex].values.push({ (state.specForm.specList[specIndex] as any).values.push({
id: specItem.id, id: specItem.id,
value: specItem.value, value: specItem.value,
picUrl: specItem.picUrl, picUrl: specItem.picUrl,
@@ -315,7 +241,7 @@ function loadData() {
} }
// SKU规格ID拼接字符串处理 // SKU规格ID拼接字符串处理
props.modelValue.skuList.forEach((sku) => { goodsInfo.value.skuList.forEach((sku: any) => {
sku.specIdArr = sku.specIds.split("_"); sku.specIdArr = sku.specIds.split("_");
}); });
@@ -335,7 +261,7 @@ function loadData() {
*/ */
function handleSpecChange() { function handleSpecChange() {
const specList = JSON.parse(JSON.stringify(state.specForm.specList)); const specList = JSON.parse(JSON.stringify(state.specForm.specList));
state.specTitles = specList.map((item) => item.name); state.specTitles = specList.map((item: any) => item.name);
} }
/** /**
@@ -380,23 +306,23 @@ function handleSpecReorder() {
*/ */
function generateSkuList() { function generateSkuList() {
// 如果规格为空生成SKU列表为空 // 如果规格为空生成SKU列表为空
if(state.specForm.specList.length==0){ if (state.specForm.specList.length == 0) {
state.skuForm.skuList = []; state.skuForm.skuList = [];
return; return;
} }
const specList = JSON.parse( const specList = JSON.parse(
JSON.stringify( JSON.stringify(
state.specForm.specList.filter((item) => item.values&&item.values.length > 0) state.specForm.specList.filter((item) => item.values && item.values.length > 0)
) )
); // 深拷贝取有属性的规格项否则笛卡尔积运算得到的SKU列表值为空 ); // 深拷贝取有属性的规格项否则笛卡尔积运算得到的SKU列表值为空
const skuList = specList.reduce( const skuList = specList.reduce(
(acc, curr) => { (acc: any, curr: any) => {
let result = []; let result = [] as any[];
acc.forEach((item) => { acc.forEach((item: any) => {
// curr => { 'id':1,'name':'颜色','values':[{id:1,value:'白色'},{id:2,value:'黑色'},{id:3,value:'蓝色'}] } // curr => { 'id':1,'name':'颜色','values':[{id:1,value:'白色'},{id:2,value:'黑色'},{id:3,value:'蓝色'}] }
curr.values.forEach((v) => { curr.values.forEach((v: any) => {
// v=>{id:1,value:'白色'} // v=>{id:1,value:'白色'}
let temp = Object.assign({}, item); let temp = Object.assign({}, item);
temp.specValues += v.value + "_"; // 规格值拼接 temp.specValues += v.value + "_"; // 规格值拼接
@@ -409,17 +335,17 @@ function generateSkuList() {
[{ specValues: "", specIds: "" }] [{ specValues: "", specIds: "" }]
); );
skuList.forEach((item) => { skuList.forEach((item: any) => {
item.specIds = item.specIds.substring(0, item.specIds.length - 1); item.specIds = item.specIds.substring(0, item.specIds.length - 1);
item.name = item.specValues item.name = item.specValues
.substring(0, item.specValues.length - 1) .substring(0, item.specValues.length - 1)
.replaceAll("_", " "); .replaceAll("_", " ");
const specIdArr = item.specIds.split("|"); const specIdArr = item.specIds.split("|");
const skus = props.modelValue.skuList.filter( const skus = goodsInfo.value.skuList.filter(
(sku) => (sku: any) =>
sku.specIdArr.length === specIdArr.length && sku.specIdArr.length === specIdArr.length &&
sku.specIdArr.every((a) => specIdArr.some((b) => a === b)) && sku.specIdArr.every((a: any) => specIdArr.some((b: any) => a === b)) &&
specIdArr.every((x) => sku.specIdArr.some((y) => x === y)) specIdArr.every((x: any) => sku.specIdArr.some((y: any) => x === y))
); // 数据库的SKU列表 ); // 数据库的SKU列表
if (skus && skus.length > 0) { if (skus && skus.length > 0) {
@@ -432,12 +358,12 @@ function generateSkuList() {
const specValueArr = item.specValues const specValueArr = item.specValues
.substring(0, item.specValues.length - 1) .substring(0, item.specValues.length - 1)
.split("_"); // ['黑','6+128G','官方标配'] .split("_"); // ['黑','6+128G','官方标配']
specValueArr.forEach((v, i) => { specValueArr.forEach((v: any, i: any) => {
const key = "specValue" + (i + 1); const key = "specValue" + (i + 1);
item[key] = v; item[key] = v;
if (i == 0 && state.specForm.specList.length > 0) { if (i == 0 && state.specForm.specList.length > 0) {
const valueIndex = state.specForm.specList[0].values.findIndex( const valueIndex = state.specForm.specList[0].values.findIndex(
(specValue) => specValue.value == v (specValue: any) => specValue.value == v
); );
if (valueIndex > -1) { if (valueIndex > -1) {
item.picUrl = state.specForm.specList[0].values[valueIndex].picUrl; item.picUrl = state.specForm.specList[0].values[valueIndex].picUrl;
@@ -456,7 +382,7 @@ function handleSpecAdd() {
ElMessage.warning("最多支持3组规格"); ElMessage.warning("最多支持3组规格");
return; return;
} }
state.specForm.specList.push({values:[]}); state.specForm.specList.push({ values: [] });
state.tagInputs.push({ value: undefined, visible: false }); state.tagInputs.push({ value: undefined, visible: false });
handleSpecReorder(); handleSpecReorder();
} }
@@ -465,7 +391,7 @@ function handleSpecAdd() {
* 删除规格 * 删除规格
* @param index * @param index
*/ */
function handleSpecRemove(index) { function handleSpecRemove(index: any) {
state.specForm.specList.splice(index, 1); state.specForm.specList.splice(index, 1);
state.tagInputs.splice(index, 1); state.tagInputs.splice(index, 1);
generateSkuList(); generateSkuList();
@@ -478,7 +404,7 @@ function handleSpecRemove(index) {
* *
* @param specIndex * @param specIndex
*/ */
function handleSpecValueAdd(specIndex) { function handleSpecValueAdd(specIndex: any) {
state.tagInputs[specIndex].visible = true; state.tagInputs[specIndex].visible = true;
} }
@@ -488,10 +414,10 @@ function handleSpecValueAdd(specIndex) {
* @param rowIndex * @param rowIndex
* @param specValueId * @param specValueId
*/ */
function handleSpecValueRemove(rowIndex, specValueId) { function handleSpecValueRemove(rowIndex: any, specValueId: any) {
const specList = JSON.parse(JSON.stringify(state.specForm.specList)); const specList = JSON.parse(JSON.stringify(state.specForm.specList));
const removeIndex = specList[rowIndex].values const removeIndex = specList[rowIndex].values
.map((item) => item.id) .map((item: any) => item.id)
.indexOf(specValueId); .indexOf(specValueId);
specList[rowIndex].values.splice(removeIndex, 1); specList[rowIndex].values.splice(removeIndex, 1);
state.specForm.specList = specList; state.specForm.specList = specList;
@@ -503,13 +429,13 @@ function handleSpecValueRemove(rowIndex, specValueId) {
/** /**
* 规格值输入 * 规格值输入
*/ */
function handleSpecValueInput(rowIndex) { function handleSpecValueInput(rowIndex: any) {
const currSpecValue = state.tagInputs[rowIndex].value; const currSpecValue = state.tagInputs[rowIndex].value;
const specValues = state.specForm.specList[rowIndex].values; const specValues = state.specForm.specList[rowIndex].values;
if ( if (
specValues && specValues &&
specValues.length > 0 && specValues.length > 0 &&
specValues.map((item) => item.value).includes(currSpecValue) specValues.map((item: any) => item.value).includes(currSpecValue)
) { ) {
ElMessage.warning("规格值重复,请重新输入"); ElMessage.warning("规格值重复,请重新输入");
return false; return false;
@@ -518,12 +444,11 @@ function handleSpecValueInput(rowIndex) {
if (specValues && specValues.length > 0) { if (specValues && specValues.length > 0) {
// 临时规格值ID tid_1_1 // 临时规格值ID tid_1_1
let maxSpecValueIndex = specValues let maxSpecValueIndex = specValues
.filter((item) => item.id.includes("tid_")) .filter((item: any) => item.id.includes("tid_"))
.map((item) => item.id.split("_")[2]) .map((item: any) => item.id.split("_")[2])
.reduce((acc, curr) => { .reduce((acc: any, curr: any) => {
return acc > curr ? acc : curr; return acc > curr ? acc : curr;
}, 0); }, 0);
console.log("maxSpecValueIndex", maxSpecValueIndex);
state.specForm.specList[rowIndex].values[specValues.length] = { state.specForm.specList[rowIndex].values[specValues.length] = {
value: currSpecValue, value: currSpecValue,
id: "tid_" + (rowIndex + 1) + "_" + ++maxSpecValueIndex, id: "tid_" + (rowIndex + 1) + "_" + ++maxSpecValueIndex,
@@ -545,7 +470,7 @@ function handleSpecValueInput(rowIndex) {
* @param cellObj 单元格对象 * @param cellObj 单元格对象
*/ */
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => { const objectSpanMethod = ({ rowIndex, columnIndex }: any) => {
let mergeRows = [1, 1, 1]; // 分别对应规格1、规格2、规格3列合并的行数 let mergeRows = [1, 1, 1]; // 分别对应规格1、规格2、规格3列合并的行数
const specLen = state.specForm.specList.filter( const specLen = state.specForm.specList.filter(
(item) => item.values && item.values.length > 0 (item) => item.values && item.values.length > 0
@@ -589,20 +514,18 @@ function submitForm() {
ElMessage.warning("未添加商品库存"); ElMessage.warning("未添加商品库存");
return false; return false;
} }
specFormRef.value.validate((specValid) => { specFormRef.value.validate((specValid: any) => {
if (specValid) { if (specValid) {
skuFormRef.value.validate((skuValid) => { skuFormRef.value.validate((skuValid: any) => {
if (skuValid) { if (skuValid) {
// openFullScreen()
// 重组商品的规格和SKU列表 // 重组商品的规格和SKU列表
let submitsData = Object.assign({}, props.modelValue); let submitsData = Object.assign({}, goodsInfo.value);
delete submitsData.specList; delete submitsData.specList;
delete submitsData.skuList; delete submitsData.skuList;
let specList = []; let specList = [] as any[];
state.specForm.specList.forEach((item) => { state.specForm.specList.forEach((item) => {
item.values.forEach((value) => { item.values.forEach((value: any) => {
value.name = item.name; value.name = item.name;
}); });
specList = specList.concat(item.values); specList = specList.concat(item.values);
@@ -613,43 +536,35 @@ function submitForm() {
submitsData.originPrice *= 100; submitsData.originPrice *= 100;
let skuList = JSON.parse(JSON.stringify(state.skuForm.skuList)); let skuList = JSON.parse(JSON.stringify(state.skuForm.skuList));
skuList.map((item) => { skuList.map((item: any) => {
item.price *= 100; item.price *= 100;
return item; return item;
}); });
submitsData.skuList = skuList; submitsData.skuList = skuList;
console.log("提交数据", submitsData); console.log("提交数据", submitsData);
const goodsId = props.modelValue.id; const goodsId = goodsInfo.value.id;
if (goodsId) { if (goodsId) {
// 编辑商品提交 // 编辑商品提交
updateGoods(goodsId, submitsData).then( updateGoods(goodsId, submitsData).then(
(res) => { () => {
router.push({ path: "/pms/goods" }); router.push({ path: "/pms/goods" });
ElNotification({ ElNotification({
title: "提示", title: "提示",
message: "编辑商品成功", message: "编辑商品成功",
type: "success", type: "success",
}); });
//closeFullScreen()
},
(err) => {
//closeFullScreen()
} }
); );
} else { } else {
// 新增商品提交 // 新增商品提交
addGoods(submitsData).then( addGoods(submitsData).then(
(response) => { () => {
router.push({ path: "/pms/goods" }); router.push({ path: "/pms/goods" });
ElNotification({ ElNotification({
title: "提示", title: "提示",
message: "新增商品成功", message: "新增商品成功",
type: "success", type: "success",
}); });
// closeFullScreen()
},
(err) => {
// closeFullScreen()
} }
); );
} }
@@ -659,8 +574,8 @@ function submitForm() {
}); });
} }
function openFullScreen() { /* function openFullScreen() {
state.loading = proxy.$loading({ state.loading = (proxy as any).$loading({
lock: true, lock: true,
text: "商品信息提交中,请等待...", text: "商品信息提交中,请等待...",
spinner: "el-icon-loading", spinner: "el-icon-loading",
@@ -670,18 +585,14 @@ function openFullScreen() {
function closeFullScreen() { function closeFullScreen() {
if (state.loading) { if (state.loading) {
state.loading.close(); (state.loading as any).close();
} }
} } */
function handlePrev() { function handlePrev() {
emit("prev"); emit("prev");
} }
function handNext() {
emit("next");
}
onMounted(() => { onMounted(() => {
loadData(); loadData();
}); });
@@ -704,7 +615,7 @@ onMounted(() => {
} }
} }
.el-form--inline .el-form-item{ .el-form--inline .el-form-item {
margin-top: 18px; margin-top: 18px;
} }
</style> </style>

View File

@@ -1,108 +1,77 @@
<template> <template>
<div class="app-container"> <div class="app-container">
<el-steps :active="active" process-status="finish" finish-status="success" simple> <el-steps :active="active" process-status="finish" finish-status="success" simple>
<el-step title="选择商品分类"/> <el-step title="选择商品分类" />
<el-step title="填写商品信息"/> <el-step title="填写商品信息" />
<el-step title="设置商品属性"/> <el-step title="设置商品属性" />
<el-step title="设置商品库存"/> <el-step title="设置商品库存" />
</el-steps> </el-steps>
<goods-category <GoodsCategory v-show="active == 0" v-model="goodsInfo" v-if="loaded == true" @prev="prev" @next="next" />
v-show="active==0" <GoodsInfo v-show="active == 1" v-model="goodsInfo" v-if="loaded == true" @prev="prev" @next="next" />
v-model="goods" <GoodsAttribute v-show="active == 2" v-model="goodsInfo" v-if="loaded == true" @prev="prev" @next="next" />
v-if="loaded==true" <GoodsStock v-show="active == 3" v-model="goodsInfo" v-if="loaded == true" @prev="prev" @next="next" />
@prev="prev"
@next="next"
/>
<goods-info
v-show="active==1"
v-model="goods"
v-if="loaded==true"
@prev="prev"
@next="next"
/>
<goods-attribute
v-show="active==2"
v-model="goods"
v-if="loaded==true"
@prev="prev"
@next="next"
/>
<goods-stock
v-show="active==3"
v-model="goods"
v-if="loaded==true"
@prev="prev"
@next="next"
/>
</div> </div>
</template> </template>
<script> <script setup lang="ts">
import { onMounted, reactive, toRefs } from "vue";
import GoodsCategory from "./components/GoodsCategory.vue"; import GoodsCategory from "./components/GoodsCategory.vue";
import GoodsInfo from "./components/GoodsInfo.vue"; import GoodsInfo from "./components/GoodsInfo.vue";
import GoodsAttribute from "./components/GoodsAttribute.vue"; import GoodsAttribute from "./components/GoodsAttribute.vue";
import GoodsStock from "./components/GoodsStock.vue"; import GoodsStock from "./components/GoodsStock.vue";
import {getGoodsFormDetail} from "@/api/pms/goods"; import { getGoodsDetail } from "@/api/pms/goods";
import { useRoute } from "vue-router";
import { GoodsDetail } from "@/types";
export default { const route = useRoute();
name: "goods-detail", const props = defineProps({
components: {GoodsStock, GoodsCategory, GoodsInfo, GoodsAttribute}, goodsId: {
props: ['goodsId'], type: String,
data() { default: () => ''
return { }
loaded: false, })
active: 0,
goods: { const state = reactive({
id: undefined, loaded: false,
name: undefined, active: 0,
categoryId: undefined, goodsInfo: {} as GoodsDetail
brandId: undefined, });
originPrice: undefined,
price: undefined, const { loaded, active, goodsInfo } = toRefs(state)
picUrl: undefined,
album: undefined, function loadData() {
description: undefined, const goodsId = route.query.goodsId as string
detail: undefined,
attrList: [], if (goodsId) {
specList: [], getGoodsDetail(goodsId).then((response) => {
skuList: [] state.goodsInfo = response.data
} state.goodsInfo.originPrice = state.goodsInfo.originPrice / 100
}; state.goodsInfo.price = state.goodsInfo.price / 100
}, state.loaded = true
created() { })
this.loadData() } else {
}, state.loaded = true
methods: {
loadData() {
const goodsId = this.$route.query.goodsId
console.log('goodsId',goodsId)
if (goodsId) {
getGoodsFormDetail(goodsId).then(response => {
this.goods = response.data
this.goods.originPrice = this.goods.originPrice / 100
this.goods.price = this.goods.price / 100
this.loaded = true
})
} else {
this.loaded = true
}
},
prev() {
if (this.active-- <= 0) {
this.active = 0;
}
},
next() {
if (this.active++ >= 3) {
this.active = 0;
}
}
} }
} }
function prev() {
if (state.active-- <= 0) {
state.active = 0;
}
}
function next() {
if (state.active++ >= 3) {
state.active = 0;
}
}
onMounted(() => {
loadData();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@@ -24,7 +24,7 @@
<el-cascader <el-cascader
v-model="queryParams.categoryId" v-model="queryParams.categoryId"
placeholder="商品分类" placeholder="商品分类"
:props="{ emitPath: false, expandTrigger: 'hover' }" :props="{ emitPath: false }"
:options="categoryOptions" :options="categoryOptions"
clearable clearable
style="width: 300px" style="width: 300px"
@@ -177,8 +177,6 @@ const state = reactive({
const { const {
loading, loading,
ids,
single,
multiple, multiple,
queryParams, queryParams,
goodsList, goodsList,

View File

@@ -3,40 +3,20 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" <el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
>新增</el-button <el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
>
<el-button
type="danger"
:icon="Delete"
:disabled="multiple"
@click="handleDelete"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item prop="title"> <el-form-item prop="title">
<el-input <el-input v-model="queryParams.title" placeholder="广告标题" clearable @keyup.enter="handleQuery" />
v-model="queryParams.title"
placeholder="广告标题"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table <el-table v-loading="loading" :data="advertList" @selection-change="handleSelectionChange" border>
v-loading="loading"
:data="advertList"
@selection-change="handleSelectionChange"
border
>
<el-table-column type="selection" min-width="5" align="center" /> <el-table-column type="selection" min-width="5" align="center" />
<el-table-column type="index" label="序号" width="80" align="center" /> <el-table-column type="index" label="序号" width="80" align="center" />
<el-table-column prop="title" min-width="100" label="广告标题" /> <el-table-column prop="title" min-width="100" label="广告标题" />
@@ -45,10 +25,7 @@
<el-popover placement="right" :width="400" trigger="hover"> <el-popover placement="right" :width="400" trigger="hover">
<img :src="scope.row.picUrl" width="400" height="400" /> <img :src="scope.row.picUrl" width="400" height="400" />
<template #reference> <template #reference>
<img <img :src="scope.row.picUrl" style="max-height: 60px; max-width: 60px" />
:src="scope.row.picUrl"
style="max-height: 60px; max-width: 60px"
/>
</template> </template>
</el-popover> </el-popover>
</template> </template>
@@ -64,57 +41,27 @@
<el-table-column prop="sort" label="排序" width="80" /> <el-table-column prop="sort" label="排序" width="80" />
<el-table-column label="操作" align="center" width="150"> <el-table-column label="操作" align="center" width="150">
<template #default="scope"> <template #default="scope">
<el-button <el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
type="primary" <el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
:icon="Edit"
circle
plain
@click.stop="handleUpdate(scope.row)"
/>
<el-button
type="danger"
:icon="Delete"
circle
plain
@click.stop="handleDelete(scope.row)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
v-show="total > 0" @pagination="handleQuery" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<!-- 表单弹窗 --> <!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="700px"> <el-dialog :title="dialog.title" v-model="dialog.visible" width="700px">
<el-form <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="100px"
>
<el-form-item label="广告标题" prop="title"> <el-form-item label="广告标题" prop="title">
<el-input v-model="formData.title" /> <el-input v-model="formData.title" />
</el-form-item> </el-form-item>
<el-form-item label="有效期" prop="beginTime"> <el-form-item label="有效期" prop="beginTime">
<el-date-picker <el-date-picker v-model="formData.beginTime" placeholder="开始时间" value-format="YYYY-MM-DD" />
v-model="formData.beginTime"
placeholder="开始时间"
value-format="YYYY-MM-DD"
/>
~ ~
<el-date-picker <el-date-picker v-model="formData.endTime" placeholder="结束时间" value-format="YYYY-MM-DD" />
v-model="formData.endTime"
placeholder="结束时间"
value-format="YYYY-MM-DD"
/>
</el-form-item> </el-form-item>
<el-form-item label="广告图片" prop="picUrl"> <el-form-item label="广告图片" prop="picUrl">
@@ -152,7 +99,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs, unref } from "vue"; import { onMounted, reactive, ref, toRefs } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus"; import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue"; import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import SingleUpload from "@/components/Upload/SingleUpload.vue"; import SingleUpload from "@/components/Upload/SingleUpload.vue";
@@ -194,7 +141,6 @@ const state = reactive({
const { const {
loading, loading,
single,
multiple, multiple,
queryParams, queryParams,
advertList, advertList,
@@ -206,7 +152,7 @@ const {
function handleQuery() { function handleQuery() {
state.loading = true; state.loading = true;
listAdvertPages(state.queryParams).then(({data}) => { listAdvertPages(state.queryParams).then(({ data }) => {
state.advertList = data.list; state.advertList = data.list;
state.total = data.total; state.total = data.total;
state.loading = false; state.loading = false;
@@ -225,7 +171,6 @@ function handleSelectionChange(selection: any) {
} }
function handleAdd() { function handleAdd() {
resetForm();
state.dialog = { state.dialog = {
title: "添加广告", title: "添加广告",
visible: true, visible: true,
@@ -248,13 +193,13 @@ function submitForm() {
if (valid) { if (valid) {
const avertId = state.formData.id; const avertId = state.formData.id;
if (avertId) { if (avertId) {
updateAdvert(avertId, state.formData).then((response) => { updateAdvert(avertId, state.formData).then(() => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
cancel(); cancel();
handleQuery(); handleQuery();
}); });
} else { } else {
addAdvert(state.formData).then((response) => { addAdvert(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
cancel(); cancel();
handleQuery(); handleQuery();
@@ -264,16 +209,9 @@ function submitForm() {
}); });
} }
/** function cancel() {
* 重置表单
*/
function resetForm() {
state.formData.id = undefined; state.formData.id = undefined;
dataFormRef.value.resetFields(); dataFormRef.value.resetFields();
}
function cancel() {
resetForm();
state.dialog.visible = false; state.dialog.visible = false;
} }

View File

@@ -3,43 +3,23 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" <el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
>新增</el-button <el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
>
<el-button
type="danger"
:icon="Delete"
:disabled="multiple"
@click="handleDelete"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item prop="clientId"> <el-form-item prop="clientId">
<el-input <el-input v-model="queryParams.clientId" placeholder="输入客户端ID" clearable style="width: 240px"
v-model="queryParams.clientId" @keyup.enter="handleQuery" />
placeholder="输入客户端ID"
clearable
style="width: 240px"
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table <el-table v-loading="loading" :data="clientList" border @selection-change="handleSelectionChange">
v-loading="loading"
:data="clientList"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="55" align="center" /> <el-table-column label="序号" type="index" width="55" align="center" />
<el-table-column label="客户端ID" prop="clientId" width="200" /> <el-table-column label="客户端ID" prop="clientId" width="200" />
@@ -47,69 +27,33 @@
<el-table-column label="域" width="100" prop="scope" /> <el-table-column label="域" width="100" prop="scope" />
<el-table-column label="自动放行" prop="autoapprove" width="100" /> <el-table-column label="自动放行" prop="autoapprove" width="100" />
<el-table-column label="授权方式" prop="authorizedGrantTypes" /> <el-table-column label="授权方式" prop="authorizedGrantTypes" />
<el-table-column <el-table-column label="认证令牌时效(单位:秒)" width="200" prop="accessTokenValidity" />
label="认证令牌时效(单位:秒)" <el-table-column label="刷新令牌时效(单位:秒)" width="200" prop="refreshTokenValidity" />
width="200"
prop="accessTokenValidity"
/>
<el-table-column
label="刷新令牌时效(单位:秒)"
width="200"
prop="refreshTokenValidity"
/>
<el-table-column label="操作" align="center" width="120"> <el-table-column label="操作" align="center" width="120">
<template #default="scope"> <template #default="scope">
<el-button <el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
type="primary" <el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
:icon="Edit"
circle
plain
@click.stop="handleUpdate(scope.row)"
/>
<el-button
type="danger"
:icon="Delete"
circle
plain
@click.stop="handleDelete(scope.row)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
v-show="total > 0" @pagination="handleQuery" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<!-- 表单弹窗 --> <!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="700px"> <el-dialog :title="dialog.title" v-model="dialog.visible" width="700px">
<el-form <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="100px"
>
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="客户端ID" prop="clientId"> <el-form-item label="客户端ID" prop="clientId">
<el-input <el-input v-model="formData.clientId" placeholder="请输入客户端ID" />
v-model="formData.clientId"
placeholder="请输入客户端ID"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="客户端密钥" prop="clientSecret"> <el-form-item label="客户端密钥" prop="clientSecret">
<el-input <el-input v-model="formData.clientSecret" placeholder="请输入客户端密钥" />
v-model="formData.clientSecret"
placeholder="请输入客户端密钥"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -132,11 +76,9 @@
<el-form-item label="授权方式" prop="authorizedGrantTypes"> <el-form-item label="授权方式" prop="authorizedGrantTypes">
<el-checkbox-group v-model="checkedAuthorizedGrantTypes"> <el-checkbox-group v-model="checkedAuthorizedGrantTypes">
<el-checkbox <el-checkbox v-for="item in authorizedGrantTypesOptions" :key="item.value" :label="item.value">{{
v-for="item in authorizedGrantTypesOptions" item.label
:key="item.value" }}
:label="item.value"
>{{ item.label }}
</el-checkbox> </el-checkbox>
</el-checkbox-group> </el-checkbox-group>
</el-form-item> </el-form-item>
@@ -144,19 +86,13 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="认证令牌时效" prop="accessTokenValidity"> <el-form-item label="认证令牌时效" prop="accessTokenValidity">
<el-input <el-input v-model="formData.accessTokenValidity" placeholder="请输入认证令牌时效" />
v-model="formData.accessTokenValidity"
placeholder="请输入认证令牌时效"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="刷新令牌时效" prop="refreshTokenValidity"> <el-form-item label="刷新令牌时效" prop="refreshTokenValidity">
<el-input <el-input v-model="formData.refreshTokenValidity" placeholder="请输入刷新令牌时效" />
v-model="formData.refreshTokenValidity"
placeholder="请输入刷新令牌时效"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -164,29 +100,19 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="回调地址" prop="webServerRedirectUri"> <el-form-item label="回调地址" prop="webServerRedirectUri">
<el-input <el-input v-model="formData.webServerRedirectUri" placeholder="请输入回调地址" />
v-model="formData.webServerRedirectUri"
placeholder="请输入回调地址"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-form-item label="权限" prop="authorities"> <el-form-item label="权限" prop="authorities">
<el-input <el-input v-model="formData.authorities" placeholder="请输入权限" />
v-model="formData.authorities"
placeholder="请输入权限"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="24"> <el-col :span="24">
<el-form-item label="扩展信息" prop="additionalInformation"> <el-form-item label="扩展信息" prop="additionalInformation">
<el-input <el-input v-model="formData.additionalInformation" type="textarea" placeholder="JSON格式" />
v-model="formData.additionalInformation"
type="textarea"
placeholder="JSON格式"
/>
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
@@ -250,7 +176,6 @@ const state = reactive({
const { const {
loading, loading,
ids, ids,
single,
multiple, multiple,
queryParams, queryParams,
clientList, clientList,
@@ -318,7 +243,7 @@ function submitForm() {
state.checkedAuthorizedGrantTypes.join(","); state.checkedAuthorizedGrantTypes.join(",");
if (state.dialog.type == "edit") { if (state.dialog.type == "edit") {
updateClient(state.formData.clientId, state.formData).then( updateClient(state.formData.clientId, state.formData).then(
(response) => { () => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
state.dialog.visible = false; state.dialog.visible = false;
cancel(); cancel();
@@ -326,7 +251,7 @@ function submitForm() {
} }
); );
} else { } else {
addClient(state.formData).then((response) => { addClient(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
cancel(); cancel();
handleQuery(); handleQuery();

View File

@@ -179,7 +179,6 @@ const state = reactive({
ids: [] as number[], ids: [] as number[],
// 非单个禁用 // 非单个禁用
single: true, single: true,
disabled: false,
loading: true, loading: true,
// 表格树数据 // 表格树数据
deptList: [] as DeptItem[], deptList: [] as DeptItem[],
@@ -205,9 +204,7 @@ const state = reactive({
}); });
const { const {
ids,
single, single,
disabled,
loading, loading,
deptList, deptList,
deptOptions, deptOptions,
@@ -257,14 +254,6 @@ async function loadDeptOptions() {
}); });
} }
/**
* 表单重置
**/
function resetForm() {
state.formData.id = undefined;
dataFormRef.value.resetFields();
}
/** /**
* 添加部门 * 添加部门
*/ */
@@ -300,15 +289,15 @@ function submitForm() {
dataForm.validate((valid: any) => { dataForm.validate((valid: any) => {
if (valid) { if (valid) {
if (state.formData.id) { if (state.formData.id) {
updateDept(state.formData.id, state.formData).then((res: any) => { updateDept(state.formData.id, state.formData).then(() => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
state.dialog.visible = false; cancel();
handleQuery(); handleQuery();
}); });
} else { } else {
addDept(state.formData).then(() => { addDept(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
state.dialog.visible = false; cancel();
handleQuery(); handleQuery();
}); });
} }
@@ -345,7 +334,8 @@ function handleDelete(row: any) {
* 取消/关闭弹窗 * 取消/关闭弹窗
**/ **/
function cancel() { function cancel() {
resetForm(); state.formData.id = undefined;
dataFormRef.value.resetFields();
state.dialog.visible = false; state.dialog.visible = false;
} }

View File

@@ -3,43 +3,23 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="state.queryParams" :inline="true"> <el-form ref="queryFormRef" :model="state.queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" <el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
>新增</el-button <el-button type="danger" :icon="Delete" :disabled="state.multiple" @click="handleDelete">删除
>
<el-button
type="danger"
:icon="Delete"
:disabled="state.multiple"
@click="handleDelete"
>删除
</el-button> </el-button>
</el-form-item> </el-form-item>
<el-form-item prop="name"> <el-form-item prop="name">
<el-input <el-input v-model="state.queryParams.name" placeholder="字典名称" clearable @keyup.enter="handleQuery" />
v-model="state.queryParams.name"
placeholder="字典名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery()">搜索</el-button>
>搜索</el-button <el-button :icon="Refresh" @click="resetQuery()">重置</el-button>
>
<el-button :icon="Refresh" @click="resetForm">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table <el-table highlight-current-row :data="dictList" v-loading="loading" @row-click="handleRowClick"
highlight-current-row @selection-change="handleSelectionChange" border>
:data="dictList"
v-loading="loading"
@row-click="handleRowClick"
@selection-change="handleSelectionChange"
border
>
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="字典名称" prop="name" width="120" /> <el-table-column label="字典名称" prop="name" width="120" />
<el-table-column label="字典编码" prop="code" /> <el-table-column label="字典编码" prop="code" />
@@ -52,45 +32,18 @@
<el-table-column label="操作" align="center" width="150"> <el-table-column label="操作" align="center" width="150">
<template #default="scope"> <template #default="scope">
<el-button <el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
type="primary" <el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
:icon="Edit"
circle
plain
@click.stop="handleUpdate(scope.row)"
/>
<el-button
type="danger"
:icon="Delete"
circle
plain
@click.stop="handleDelete(scope.row)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
v-show="total > 0" @pagination="handleQuery" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<!-- 弹窗表单 --> <!-- 弹窗表单 -->
<el-dialog <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" @close="cancel">
:title="dialog.title" <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="80px">
v-model="dialog.visible"
width="500px"
@close="cancel"
>
<el-form
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="80px"
>
<el-form-item label="字典名称" prop="name"> <el-form-item label="字典名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入字典名称" /> <el-input v-model="formData.name" placeholder="请输入字典名称" />
</el-form-item> </el-form-item>
@@ -104,12 +57,8 @@
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input <el-input v-model="formData.remark" type="textarea" placeholder="请输入内容"
v-model="formData.remark" :autosize="{ minRows: 2, maxRows: 4 }" />
type="textarea"
placeholder="请输入内容"
:autosize="{ minRows: 2, maxRows: 4 }"
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@@ -123,7 +72,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue"; import { onMounted, reactive, ref, toRefs, defineEmits } from "vue";
import { import {
listDictPages, listDictPages,
getDictFormDetail, getDictFormDetail,
@@ -134,7 +83,6 @@ import {
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue"; import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus"; import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { Dialog, Dict, DictFormData, DictQueryParam } from "@/types"; import { Dialog, Dict, DictFormData, DictQueryParam } from "@/types";
import { status } from "nprogress";
const queryFormRef = ref(ElForm); const queryFormRef = ref(ElForm);
const dataFormRef = ref(ElForm); const dataFormRef = ref(ElForm);
@@ -155,7 +103,7 @@ const state = reactive({
} as DictQueryParam, } as DictQueryParam,
dictList: [] as Dict[], dictList: [] as Dict[],
total: 0, total: 0,
dialog: {visible:false} as Dialog, dialog: { visible: false } as Dialog,
formData: { formData: {
status: 1, status: 1,
} as DictFormData, } as DictFormData,
@@ -168,14 +116,11 @@ const state = reactive({
const { const {
total, total,
dialog, dialog,
ids,
loading, loading,
single,
multiple,
queryParams,
dictList, dictList,
formData, formData,
rules, rules,
queryParams
} = toRefs(state); } = toRefs(state);
function handleQuery() { function handleQuery() {
@@ -221,15 +166,15 @@ function submitForm() {
dataFormRef.value.validate((isValid: boolean) => { dataFormRef.value.validate((isValid: boolean) => {
if (isValid) { if (isValid) {
if (state.formData.id) { if (state.formData.id) {
updateDict(state.formData.id, state.formData).then((response) => { updateDict(state.formData.id, state.formData).then(() => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
state.dialog.visible = false; cancel()
handleQuery(); handleQuery();
}); });
} else { } else {
addDict(state.formData).then((response) => { addDict(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
state.dialog.visible = false; cancel()
handleQuery(); handleQuery();
}); });
} }
@@ -237,12 +182,9 @@ function submitForm() {
}); });
} }
function resetForm() { function cancel() {
state.formData.id = undefined; state.formData.id = undefined;
dataFormRef.value.resetFields(); dataFormRef.value.resetFields();
}
function cancel() {
state.dialog.visible = false; state.dialog.visible = false;
} }

View File

@@ -3,39 +3,20 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" <el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
>新增</el-button <el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
>
<el-button
type="danger"
:icon="Delete"
:disabled="multiple"
@click="handleDelete"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item prop="name"> <el-form-item prop="name">
<el-input <el-input v-model="queryParams.name" placeholder="数据项名称" clearable />
v-model="queryParams.name"
placeholder="数据项名称"
clearable
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table <el-table :data="dictItemList" v-loading="loading" border @selection-change="handleSelectionChange">
:data="dictItemList"
v-loading="loading"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" min-width="5%" /> <el-table-column type="selection" min-width="5%" />
<el-table-column label="数据项名称" prop="name" /> <el-table-column label="数据项名称" prop="name" />
<el-table-column label="数据项值" prop="value" /> <el-table-column label="数据项值" prop="value" />
@@ -47,76 +28,37 @@
</el-table-column> </el-table-column>
<el-table-column label="操作" align="center"> <el-table-column label="操作" align="center">
<template #default="scope"> <template #default="scope">
<el-button <el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
type="primary" <el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
:icon="Edit"
circle
plain
@click.stop="handleUpdate(scope.row)"
/>
<el-button
type="danger"
:icon="Delete"
circle
plain
@click.stop="handleDelete(scope.row)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
v-show="state.total > 0" @pagination="handleQuery" />
:total="state.total"
v-model:page="state.queryParams.pageNum"
v-model:limit="state.queryParams.pageSize"
@pagination="handleQuery"
/>
<!-- 表单弹窗 --> <!-- 表单弹窗 -->
<el-dialog <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" @close="cancel">
:title="dialog.title" <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
v-model="dialog.visible"
width="500px"
@close="cancel"
>
<el-form
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="100px"
>
<el-form-item label="字典名称"> <el-form-item label="字典名称">
<el-input v-model="props.dictName" :disabled="true" />
</el-form-item> </el-form-item>
<el-form-item label="字典项名称" prop="name"> <el-form-item label="字典项名称" prop="name">
<el-input <el-input v-model="formData.name" placeholder="请输入字典项名称" />
v-model="state.formData.name"
placeholder="请输入字典项名称"
/>
</el-form-item> </el-form-item>
<el-form-item label="字典项值" prop="value"> <el-form-item label="字典项值" prop="value">
<el-input <el-input v-model="formData.value" placeholder="请输入字典项值" />
v-model="state.formData.value"
placeholder="请输入字典项值"
/>
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number <el-input-number v-model="formData.sort" style="width: 80px" controls-position="right" :min="0" />
v-model="state.formData.sort"
style="width: 80px"
controls-position="right"
:min="0"
/>
</el-form-item> </el-form-item>
<el-form-item label="状态" prop="status"> <el-form-item label="状态" prop="status">
<el-radio-group v-model="state.formData.status"> <el-radio-group v-model="formData.status">
<el-radio :label="1">正常</el-radio> <el-radio :label="1">正常</el-radio>
<el-radio :label="0">停用</el-radio> <el-radio :label="0">停用</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item label="备注" prop="remark"> <el-form-item label="备注" prop="remark">
<el-input v-model="state.formData.remark" type="textarea"></el-input> <el-input v-model="formData.remark" type="textarea"></el-input>
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@@ -130,7 +72,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs, unref, watch } from "vue"; import { onMounted, reactive, ref, toRefs, watch } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus"; import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { import {
Dialog, Dialog,
@@ -147,15 +89,18 @@ import {
} from "@/api/system/dict"; } from "@/api/system/dict";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue"; import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
const props = defineProps({ const props = defineProps({
dictCode: { dictCode: {
type: String, type: String,
default: "", default: () => {
return ''
}
}, },
dictName: { dictName: {
type: String, type: String,
default: "", default: () => {
return ''
}
}, },
}); });
@@ -179,32 +124,33 @@ const state = reactive({
single: true, single: true,
// 非多个禁用 // 非多个禁用
multiple: true, multiple: true,
total: 0,
queryParams: { pageNum: 1, pageSize: 10 } as DictItemQueryParam, queryParams: { pageNum: 1, pageSize: 10 } as DictItemQueryParam,
dictItemList: [] as DictItem[], dictItemList: [] as DictItem[],
total: 0,
dialog: { visible: false } as Dialog, dialog: { visible: false } as Dialog,
formData: { formData: {
dictCode: props.dictCode, dictCode: props.dictCode,
dictName: props.dictName,
status: 1, status: 1,
sort: 1, sort: 1,
} as DictItemFormData, } as DictItemFormData,
rules: { rules: {
name: [{ required: true, message: "请输入字典项名称", trigger: "blur" }], name: [{ required: true, message: "请输入字典项名称", trigger: "blur" }],
value: [{ required: true, message: "请输入字典项值", trigger: "blur" }], value: [{ required: true, message: "请输入字典项值", trigger: "blur" }]
}, },
localDictCode: props.dictCode,
localDictName: props.dictName
}); });
const { const {
loading, loading,
ids,
single,
multiple, multiple,
queryParams, queryParams,
dictItemList, dictItemList,
total,
dialog, dialog,
formData, formData,
rules, rules,
total
} = toRefs(state); } = toRefs(state);
function handleQuery() { function handleQuery() {
@@ -260,15 +206,15 @@ function submitForm() {
dataFormRef.value.validate((isValid: boolean) => { dataFormRef.value.validate((isValid: boolean) => {
if (isValid) { if (isValid) {
if (state.formData.id) { if (state.formData.id) {
updateDictItem(state.formData.id, state.formData).then((response) => { updateDictItem(state.formData.id, state.formData).then(() => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
state.dialog.visible = false; cancel();
handleQuery(); handleQuery();
}); });
} else { } else {
addDictItem(state.formData).then((response) => { addDictItem(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
state.dialog.visible = false; cancel();
handleQuery(); handleQuery();
}); });
} }
@@ -276,13 +222,10 @@ function submitForm() {
}); });
} }
function resetForm() {
state.formData.id = undefined;
dataFormRef.value.resetFields();
}
function cancel() { function cancel() {
state.dialog.visible = false; state.dialog.visible = false;
state.formData.id = undefined;
dataFormRef.value.resetFields();
} }
function handleDelete(row: any) { function handleDelete(row: any) {

View File

@@ -17,11 +17,11 @@
<template #header> <template #header>
<svg-icon color="#333" icon-class="dict"/> <svg-icon color="#333" icon-class="dict"/>
<span style="margin:0 5px;">字典数据项</span> <span style="margin:0 5px;">字典数据项</span>
<el-tag type="success" v-if=" state.dictCode" size="small">{{ state.dictName }}</el-tag> <el-tag type="success" v-if="dictCode" size="small">{{dictName }}</el-tag>
<el-tag type="warning" v-else size="small">未选择字典</el-tag> <el-tag type="warning" v-else size="small">未选择字典</el-tag>
</template> </template>
<!-- 字典项组件 --> <!-- 字典项组件 -->
<dict-item :dictName="state.dictName" :dictCode='state.dictCode'/> <dict-item :dictName="dictName" :dictCode='dictCode'/>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>

View File

@@ -3,43 +3,25 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" <el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
>新增</el-button
>
</el-form-item> </el-form-item>
<el-form-item prop="name"> <el-form-item prop="name">
<el-input <el-input v-model="queryParams.name" placeholder="菜单名称" clearable @keyup.enter="handleQuery" />
v-model="queryParams.name"
placeholder="菜单名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table <el-table v-loading="loading" :data="menuList" highlight-current-row
v-loading="loading" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @row-click="handleRowClick" row-key="id"
:data="menuList" border>
highlight-current-row
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
@row-click="handleRowClick"
row-key="id"
border
>
<el-table-column label="菜单名称"> <el-table-column label="菜单名称">
<template #default="scope"> <template #default="scope">
<svg-icon <svg-icon color="#333" :icon-class="scope.row.icon ? scope.row.icon : 'build'" />
color="#333"
:icon-class="scope.row.icon ? scope.row.icon : 'build'"
/>
{{ scope.row.name }} {{ scope.row.name }}
</template> </template>
</el-table-column> </el-table-column>
@@ -53,45 +35,18 @@
<el-table-column label="操作" align="center" width="200"> <el-table-column label="操作" align="center" width="200">
<template #default="scope"> <template #default="scope">
<el-button <el-button type="success" :icon="Plus" circle plain @click.stop="handleAdd(scope.row)" />
type="success" <el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
:icon="Plus" <el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
circle
plain
@click.stop="handleAdd(scope.row)"
/>
<el-button
type="primary"
:icon="Edit"
circle
plain
@click.stop="handleUpdate(scope.row)"
/>
<el-button
type="danger"
:icon="Delete"
circle
plain
@click.stop="handleDelete(scope.row)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 弹窗表单 --> <!-- 弹窗表单 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="750px"> <el-dialog :title="dialog.title" v-model="dialog.visible" width="750px">
<el-form <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="100px"
>
<el-form-item label="父级菜单" prop="parentId"> <el-form-item label="父级菜单" prop="parentId">
<tree-select <tree-select v-model="formData.parentId" :options="menuOptions" placeholder="选择上级菜单" />
v-model="formData.parentId"
:options="menuOptions"
placeholder="选择上级菜单"
/>
</el-form-item> </el-form-item>
<el-form-item label="菜单名称" prop="name"> <el-form-item label="菜单名称" prop="name">
@@ -110,51 +65,24 @@
</el-form-item> </el-form-item>
<el-form-item v-if="!isExternalPath" label="页面路径" prop="component"> <el-form-item v-if="!isExternalPath" label="页面路径" prop="component">
<el-input <el-input v-model="formData.component" placeholder="system/user/index" style="width: 95%">
v-model="formData.component" <template v-if="formData.parentId != 0" #prepend>src/views/</template>
placeholder="system/user/index"
style="width: 95%"
>
<template v-if="formData.parentId != 0" #prepend
>src/views/</template
>
<template v-if="formData.parentId != 0" #append>.vue</template> <template v-if="formData.parentId != 0" #append>.vue</template>
</el-input> </el-input>
<el-tooltip <el-tooltip effect="dark" content="请输入组件路径,如果是父组件填写 Layout 即可" placement="right">
effect="dark" <i class="el-icon-info" style="margin-left: 10px; color: darkseagreen"></i>
content="请输入组件路径,如果是父组件填写 Layout 即可"
placement="right"
>
<i
class="el-icon-info"
style="margin-left: 10px; color: darkseagreen"
></i>
</el-tooltip> </el-tooltip>
</el-form-item> </el-form-item>
<el-form-item label="图标" prop="icon"> <el-form-item label="图标" prop="icon">
<el-popover <el-popover placement="bottom-start" :width="570" trigger="click" visible="iconSelectVisible">
placement="bottom-start"
:width="570"
trigger="click"
v-model:visible="iconSelectVisible"
>
<icon-select ref="iconSelectRef" @selected="selected" /> <icon-select ref="iconSelectRef" @selected="selected" />
<template #reference> <template #reference>
<el-input <el-input v-model="formData.icon" placeholder="点击选择图标" readonly @click="iconSelectVisible = true">
v-model="formData.icon"
placeholder="点击选择图标"
readonly
@click="iconSelectVisible = true"
>
<template #prefix> <template #prefix>
<svg-icon <svg-icon :icon-class="formData.icon ? formData.icon : 'color'" class="el-input__icon"
:icon-class="formData.icon ? formData.icon : 'color'" style="margin: auto" color="#999" />
class="el-input__icon"
style="margin: auto"
color="#999"
/>
</template> </template>
</el-input> </el-input>
</template> </template>
@@ -169,20 +97,11 @@
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number <el-input-number v-model="formData.sort" style="width: 100px" controls-position="right" :min="0" />
v-model="formData.sort"
style="width: 100px"
controls-position="right"
:min="0"
/>
</el-form-item> </el-form-item>
<el-form-item label="跳转路径"> <el-form-item label="跳转路径">
<el-input <el-input v-model="formData.redirect" placeholder="请输入跳转路径" maxlength="50" />
v-model="formData.redirect"
placeholder="请输入跳转路径"
maxlength="50"
/>
</el-form-item> </el-form-item>
</el-form> </el-form>
@@ -197,7 +116,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, ref, unref, onMounted, toRefs } from "vue"; import { reactive, ref, onMounted, toRefs, defineEmits } from "vue";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue"; import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus"; import { ElForm, ElMessage, ElMessageBox } from "element-plus";
@@ -240,8 +159,7 @@ const state = reactive({
multiple: true, multiple: true,
queryParams: {} as MenuQueryParam, queryParams: {} as MenuQueryParam,
menuList: [] as MenuItem[], menuList: [] as MenuItem[],
total: 0, dialog: { visible: false } as Dialog,
dialog: {visible:false} as Dialog,
formData: { formData: {
parentId: 0, parentId: 0,
visible: 1, visible: 1,
@@ -256,16 +174,13 @@ const state = reactive({
menuOptions: [] as Option[], menuOptions: [] as Option[],
currentRow: undefined, currentRow: undefined,
isExternalPath: false, isExternalPath: false,
iconSelectVisible: false, iconSelectVisible: false
}); });
const { const {
loading, loading,
single,
multiple,
queryParams, queryParams,
menuList, menuList,
total,
dialog, dialog,
formData, formData,
rules, rules,
@@ -307,15 +222,7 @@ function resetQuery() {
handleQuery(); handleQuery();
} }
function handleSelectionChange(selection: any) {
state.ids = selection.map((item: any) => item.id);
state.single = selection.length !== 1;
state.multiple = !selection.length;
}
function handleRowClick(row: any) { function handleRowClick(row: any) {
console.log('handleRowClick',row)
state.currentRow = JSON.parse(JSON.stringify(row)); state.currentRow = JSON.parse(JSON.stringify(row));
emit("menuClick", row); emit("menuClick", row);
} }
@@ -367,15 +274,15 @@ function submitForm() {
dataFormRef.value.validate((isValid: boolean) => { dataFormRef.value.validate((isValid: boolean) => {
if (isValid) { if (isValid) {
if (state.formData.id) { if (state.formData.id) {
updateMenu(state.formData.id, state.formData).then((response) => { updateMenu(state.formData.id, state.formData).then(() => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
state.dialog.visible = false; cancel();
handleQuery(); handleQuery();
}); });
} else { } else {
addMenu(state.formData).then((response) => { addMenu(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
state.dialog.visible = false; cancel();
handleQuery(); handleQuery();
}); });
} }
@@ -399,29 +306,18 @@ function handleDelete(row: any) {
.catch(() => ElMessage.info("已取消删除")); .catch(() => ElMessage.info("已取消删除"));
} }
/**
* 重置表单
*/
function resetForm() {
state.formData.id = undefined;
dataFormRef.value.resetFields();
}
/** /**
* 取消关闭弹窗 * 取消关闭弹窗
*/ */
function cancel() { function cancel() {
state.formData.id = undefined;
dataFormRef.value.resetFields();
state.dialog.visible = false; state.dialog.visible = false;
resetForm();
} }
/** /**
* 显示图标选择下拉 * 选择图标后事件
*/ */
function showIconSelect() {
state.iconSelectVisible = true;
}
function selected(name: string) { function selected(name: string) {
state.formData.icon = name; state.formData.icon = name;
state.iconSelectVisible = false; state.iconSelectVisible = false;

View File

@@ -3,44 +3,20 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-button <el-button type="success" :icon="Plus" :disabled="!menuId" @click="handleAdd">新增</el-button>
type="success" <el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
:icon="Plus"
:disabled="!menuId"
@click="handleAdd"
>新增</el-button
>
<el-button
type="danger"
:icon="Delete"
:disabled="multiple"
@click="handleDelete"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item prop="name"> <el-form-item prop="name">
<el-input <el-input v-model="queryParams.name" placeholder="权限名称" clearable @keyup.enter="handleQuery" />
v-model="queryParams.name"
placeholder="权限名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table <el-table :data="permList" v-loading="loading" @selection-change="handleSelectionChange" border>
:data="permList"
v-loading="loading"
@selection-change="handleSelectionChange"
border
>
<el-table-column type="selection" width="40" align="center" /> <el-table-column type="selection" width="40" align="center" />
<el-table-column label="权限名称" prop="name" width="150" /> <el-table-column label="权限名称" prop="name" width="150" />
<el-table-column label="URL权限" align="center"> <el-table-column label="URL权限" align="center">
@@ -51,41 +27,19 @@
<el-table-column label="按钮权限" prop="btnPerm" width="200" /> <el-table-column label="按钮权限" prop="btnPerm" width="200" />
<el-table-column label="操作" align="center" width="150"> <el-table-column label="操作" align="center" width="150">
<template #default="scope"> <template #default="scope">
<el-button <el-button type="primary" :icon="Edit" circle plain @click="handleUpdate(scope.row)" />
type="primary" <el-button type="danger" :icon="Delete" circle plain @click="handleDelete(scope.row)" />
:icon="Edit"
circle
plain
@click="handleUpdate(scope.row)"
/>
<el-button
type="danger"
:icon="Delete"
circle
plain
@click="handleDelete(scope.row)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination v-show="total > 0" :total="total" :v-model:page="queryParams.pageNum" :v-model:limit="queryParams.pageSize"
v-show="total > 0" @pagination="handleQuery" />
:total="total"
:page="queryParams.pageNum"
:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<!-- 表单弹窗 --> <!-- 表单弹窗 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="700px"> <el-dialog :title="dialog.title" v-model="dialog.visible" width="700px">
<el-form <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="120px">
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="120px"
>
<el-form-item label="权限名称" prop="name"> <el-form-item label="权限名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入权限名称" /> <el-input v-model="formData.name" placeholder="请输入权限名称" />
</el-form-item> </el-form-item>
@@ -93,37 +47,21 @@
<el-form-item label="URL权限标识" prop="urlPerm"> <el-form-item label="URL权限标识" prop="urlPerm">
<el-input placeholder="/api/v1/users" v-model="urlPerm.requestPath"> <el-input placeholder="/api/v1/users" v-model="urlPerm.requestPath">
<template #prepend> <template #prepend>
<el-select <el-select v-model="urlPerm.serviceName" style="width: 130px" placeholder="所属服务" clearable>
v-model="urlPerm.serviceName" <el-option v-for="item in microServiceOptions" :key="item.value" :value="item.value"
style="width: 130px" :label="item.label" />
placeholder="所属服务"
clearable
>
<el-option
v-for="item in microServiceOptions"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select> </el-select>
<el-select <el-select v-model="urlPerm.requestMethod" style="width: 120px; margin-left: 20px" placeholder="请求方式"
v-model="urlPerm.requestMethod" clearable>
style="width: 120px; margin-left: 20px" <el-option v-for="item in requestMethodOptions" :key="item.value" :value="item.value"
placeholder="请求方式" :label="item.label" />
clearable
>
<el-option
v-for="item in requestMethodOptions"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select> </el-select>
</template> </template>
</el-input> </el-input>
<el-link v-show="urlPerm.requestMethod"> <el-link v-show="urlPerm.requestMethod">
{{ urlPerm.requestMethod }}:/{{ urlPerm.serviceName {{ urlPerm.requestMethod }}:/{{
urlPerm.serviceName
}}{{ urlPerm.requestPath }} }}{{ urlPerm.requestPath }}
</el-link> </el-link>
</el-form-item> </el-form-item>
@@ -143,22 +81,25 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import {
listPermPages,
getPermFormDetail,
addPerm,
updatePerm,
deletePerms,
} from "@/api/system/perm";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { import {
onMounted, onMounted,
watch, watch,
reactive, reactive,
ref, ref,
getCurrentInstance, getCurrentInstance,
toRefs, toRefs
} from "vue"; } from "vue";
import {
listPermPages,
getPermFormDetail,
addPerm,
updatePerm,
deletePerms
} from "@/api/system/perm";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus"; import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { import {
Dialog, Dialog,
@@ -176,7 +117,9 @@ const dataFormRef = ref(ElForm);
const props = defineProps({ const props = defineProps({
menuId: { menuId: {
type: String, type: String,
default: "", default: () => {
return ''
}
}, },
}); });
@@ -216,14 +159,12 @@ const state = reactive({
urlPerm: { urlPerm: {
requestMethod: "", requestMethod: "",
serviceName: "", serviceName: "",
requestPath: "", requestPath: ""
}, },
}); });
const { const {
loading, loading,
ids,
single,
multiple, multiple,
permList, permList,
total, total,
@@ -280,7 +221,7 @@ function handleAdd() {
loadDictOptions(); loadDictOptions();
state.dialog = { state.dialog = {
title: "添加权限", title: "添加权限",
visible: true, visible: true
}; };
} }
@@ -337,13 +278,13 @@ function submitForm() {
state.formData.menuId = props.menuId; state.formData.menuId = props.menuId;
if (state.formData.id) { if (state.formData.id) {
updatePerm(state.formData.id, state.formData).then((response) => { updatePerm(state.formData.id, state.formData).then(() => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
cancel(); cancel();
handleQuery(); handleQuery();
}); });
} else { } else {
addPerm(state.formData).then((response) => { addPerm(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
cancel(); cancel();
handleQuery(); handleQuery();

View File

@@ -4,44 +4,26 @@
<el-form-item> <el-form-item>
<el-row> <el-row>
<el-col :span="16"> <el-col :span="16">
<el-button <el-button type="success" plain :icon="Switch" @click="toggleExpandAll">展开/折叠</el-button>
type="success"
plain
:icon="Switch"
@click="toggleExpandAll"
>展开/折叠</el-button
>
</el-col> </el-col>
<el-col :span="8" style="text-align: right"> <el-col :span="8" style="text-align: right">
<el-button type="primary" :icon="Check" @click="handleSubmit" <el-button type="primary" :icon="Check" @click="handleSubmit">提交</el-button>
>提交</el-button
>
</el-col> </el-col>
</el-row> </el-row>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-tree <el-tree ref="menuRef" v-if="refreshTree" :default-expanded-keys="expandedKeys" :default-expand-all="isExpandAll"
ref="menuRef" :data="menuOptions" show-checkbox node-key="id" empty-text="加载菜单中..." :check-strictly="checkStrictly"
v-if="refreshTree" highlight-current @node-click="handleNodeClick" />
:default-expanded-keys="expandedKeys"
:default-expand-all="isExpandAll"
:data="menuOptions"
show-checkbox
node-key="id"
empty-text="加载菜单中..."
:check-strictly="checkStrictly"
highlight-current
@node-click="handleNodeClick"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { nextTick, onMounted, reactive, ref, toRefs, watch } from "vue";
import { listSelectMenus } from "@/api/system/menu"; import { listSelectMenus } from "@/api/system/menu";
import { listRoleMenuIds, updateRoleMenu } from "@/api/system/role"; import { listRoleMenuIds, updateRoleMenu } from "@/api/system/role";
import { nextTick, onMounted, reactive, ref, toRefs, watch } from "vue"; import { ElTree, ElMessage } from "element-plus";
import { ElTree, ElMessage, ElMessageBox } from "element-plus";
import { Switch, Check } from "@element-plus/icons-vue"; import { Switch, Check } from "@element-plus/icons-vue";
import { Option } from "@/types"; import { Option } from "@/types";
@@ -49,7 +31,9 @@ const emit = defineEmits(["menuClick"]);
const props = defineProps({ const props = defineProps({
role: { role: {
type: Object, type: Object,
default: {}, default: () => {
return {}
},
}, },
}); });
@@ -57,7 +41,7 @@ const menuRef = ref(ElTree); // 属性名必须和元素的ref属性值一致
watch( watch(
() => props.role.id as any, () => props.role.id as any,
(newVal, oldVal) => { () => {
const roleId = props.role.id; const roleId = props.role.id;
if (roleId) { if (roleId) {
state.checkStrictly = true; state.checkStrictly = true;

View File

@@ -3,69 +3,54 @@
<div v-if="permissionOptions.length > 0"> <div v-if="permissionOptions.length > 0">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<el-checkbox <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange">全选
:indeterminate="isIndeterminate"
v-model="checkAll"
@change="handleCheckAllChange"
>全选
</el-checkbox> </el-checkbox>
</el-col> </el-col>
<el-col :span="12" style="text-align: right"> <el-col :span="12" style="text-align: right">
<el-button type="primary" :icon="Check" @click="handleSubmit" <el-button type="primary" :icon="Check" @click="handleSubmit">提交</el-button>
>提交</el-button
>
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col <el-col :span="8" v-for="item in permissionOptions" style="margin-top: 20px" :key="item.id">
:span="8" <el-checkbox border v-model="item.checked" :label="item.id" :key="item.id" @change="handleCheckedPermChange">
v-for="item in permissionOptions"
style="margin-top: 20px"
:key="item.id"
>
<el-checkbox
border
v-model="item.checked"
:label="item.id"
:key="item.id"
@change="handleCheckedPermChange"
>
{{ item.name }} {{ item.name }}
</el-checkbox> </el-checkbox>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
<div style="text-align: center" v-else> <div style="text-align: center" v-else>
<el-empty <el-empty :description="
:description=" !role
!role ? '请选择角色'
? '请选择角色' : !menu
: !menu
? '请选择菜单' ? '请选择菜单'
: '暂无数据,您可在【菜单管理】配置权限数据' : '暂无数据,您可在【菜单管理】配置权限数据'
" "></el-empty>
></el-empty>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, toRefs, watch } from "vue";
import { listPerms } from "@/api/system/perm"; import { listPerms } from "@/api/system/perm";
import { listRolePerms, saveRolePerms } from "@/api/system/role"; import { listRolePerms, saveRolePerms } from "@/api/system/role";
import { onMounted, reactive, toRefs, watch } from "vue";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import { Check } from "@element-plus/icons-vue"; import { Check } from "@element-plus/icons-vue";
import { PermQueryParam, MenuItem } from "@/types"; import { PermQueryParam } from "@/types";
const props = defineProps({ const props = defineProps({
role: { role: {
type: Object, type: Object,
default: {}, default: () => {
return {}
}
}, },
menu: { menu: {
type: Object, type: Object,
default: {}, default: () => {
return {}
}
}, },
}); });
@@ -86,10 +71,9 @@ const state = reactive({
checkedPerms: [], checkedPerms: [],
}); });
const { permissionOptions, isIndeterminate, checkAll, checkedPerms } = const { permissionOptions, isIndeterminate, checkAll } = toRefs(state);
toRefs(state);
function handleCheckAllChange(checked: Boolean) { function handleCheckAllChange(checked: boolean) {
state.isIndeterminate = false; state.isIndeterminate = false;
if (checked) { if (checked) {
state.permissionOptions.map((item) => (item.checked = true)); state.permissionOptions.map((item) => (item.checked = true));
@@ -99,7 +83,7 @@ function handleCheckAllChange(checked: Boolean) {
} }
} }
function handleCheckedPermChange(value: any) { function handleCheckedPermChange() {
const checkedCount = state.permissionOptions.filter( const checkedCount = state.permissionOptions.filter(
(item) => item.checked (item) => item.checked
).length; ).length;
@@ -116,7 +100,7 @@ function loadData() {
state.loading = true; state.loading = true;
const params = { menuId: props.menu.id } as PermQueryParam; const params = { menuId: props.menu.id } as PermQueryParam;
listPerms(params).then(({data}) => { listPerms(params).then(({ data }) => {
state.permissionOptions = data; state.permissionOptions = data;
listRolePerms(props.role.id, props.menu.id).then((response) => { listRolePerms(props.role.id, props.menu.id).then((response) => {
const checkedPermIds = response.data; const checkedPermIds = response.data;
@@ -150,6 +134,7 @@ function resetData() {
onMounted(() => { onMounted(() => {
loadData(); loadData();
}); });
</script> </script>
<style scoped> <style scoped>

View File

@@ -3,90 +3,41 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-button type="success" :icon="Plus" @click="handleAdd" <el-button type="success" :icon="Plus" @click="handleAdd">新增</el-button>
>新增</el-button <el-button type="danger" :icon="Delete" :disabled="multiple" @click="handleDelete">删除</el-button>
>
<el-button
type="danger"
:icon="Delete"
:disabled="multiple"
@click="handleDelete"
>删除</el-button
>
</el-form-item> </el-form-item>
<el-form-item prop="name"> <el-form-item prop="name">
<el-input <el-input v-model="queryParams.name" placeholder="角色名称" clearable @keyup.enter="handleQuery" />
v-model="queryParams.name"
placeholder="角色名称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<!-- 数据表格 --> <!-- 数据表格 -->
<el-table <el-table ref="dataTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange"
ref="dataTableRef" @row-click="handleRowClick" highlight-current-row border>
v-loading="loading"
:data="roleList"
@selection-change="handleSelectionChange"
@row-click="handleRowClick"
highlight-current-row
border
>
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="55" align="center" />
<el-table-column label="角色名称" prop="name" /> <el-table-column label="角色名称" prop="name" />
<el-table-column label="角色编码" prop="code" /> <el-table-column label="角色编码" prop="code" />
<el-table-column label="操作" align="center" width="120"> <el-table-column label="操作" align="center" width="120">
<template #default="scope"> <template #default="scope">
<el-button <el-button type="primary" :icon="Edit" circle plain @click.stop="handleUpdate(scope.row)" />
type="primary" <el-button type="danger" :icon="Delete" circle plain @click.stop="handleDelete(scope.row)" />
:icon="Edit"
circle
plain
@click.stop="handleUpdate(scope.row)"
/>
<el-button
type="danger"
:icon="Delete"
circle
plain
@click.stop="handleDelete(scope.row)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
v-show="total > 0" @pagination="handleQuery" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
<!-- 表单弹窗 --> <!-- 表单弹窗 -->
<el-dialog <el-dialog :title="dialog.title" v-model="dialog.visible" @close="cancel" width="450px">
:title="dialog.title" <el-form ref="dataFormRef" :model="formData" :rules="rules" label-width="100px">
v-model="dialog.visible"
@close="cancel"
width="450px"
>
<el-form
ref="dataFormRef"
:model="formData"
:rules="rules"
label-width="100px"
>
<el-form-item label="角色名称" prop="name"> <el-form-item label="角色名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入角色名称" /> <el-input v-model="formData.name" placeholder="请输入角色名称" />
</el-form-item> </el-form-item>
@@ -96,12 +47,7 @@
</el-form-item> </el-form-item>
<el-form-item label="排序" prop="sort"> <el-form-item label="排序" prop="sort">
<el-input-number <el-input-number v-model="formData.sort" controls-position="right" :min="0" style="width: 100px" />
v-model="formData.sort"
controls-position="right"
:min="0"
style="width: 100px"
/>
</el-form-item> </el-form-item>
<el-form-item label="状态"> <el-form-item label="状态">
@@ -123,6 +69,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref, toRefs, defineEmits } from "vue";
import { import {
listRolePages, listRolePages,
updateRole, updateRole,
@@ -130,7 +77,6 @@ import {
addRole, addRole,
deleteRoles, deleteRoles,
} from "@/api/system/role"; } from "@/api/system/role";
import { onMounted, reactive, ref, toRefs, unref } from "vue";
import { ElForm, ElMessage, ElMessageBox } from "element-plus"; import { ElForm, ElMessage, ElMessageBox } from "element-plus";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue"; import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue";
import { RoleFormData, RoleItem, RoleQueryParam } from "@/types"; import { RoleFormData, RoleItem, RoleQueryParam } from "@/types";
@@ -166,7 +112,6 @@ const state = reactive({
const { const {
loading, loading,
single,
multiple, multiple,
queryParams, queryParams,
roleList, roleList,
@@ -224,16 +169,16 @@ function submitForm() {
if (valid) { if (valid) {
if (state.formData.id) { if (state.formData.id) {
updateRole(state.formData.id as any, state.formData).then( updateRole(state.formData.id as any, state.formData).then(
(response) => { () => {
ElMessage.success("修改成功"); ElMessage.success("修改成功");
state.dialog.visible = false; cancel();
handleQuery(); handleQuery();
} }
); );
} else { } else {
addRole(state.formData).then((response) => { addRole(state.formData).then(() => {
ElMessage.success("新增成功"); ElMessage.success("新增成功");
state.dialog.visible = false; cancel();
handleQuery(); handleQuery();
}); });
} }
@@ -244,14 +189,9 @@ function submitForm() {
/** /**
* 重置表单 * 重置表单
*/ */
function resetForm() {
dataFormRef.value.resetFields();
handleQuery();
}
function cancel() { function cancel() {
resetForm();
state.dialog.visible = false; state.dialog.visible = false;
dataFormRef.value.resetFields();
} }
function handleDelete(row: any) { function handleDelete(row: any) {

View File

@@ -7,7 +7,7 @@
<svg-icon color="#333" icon-class="role" /> <svg-icon color="#333" icon-class="role" />
角色列表 角色列表
</template> </template>
<role ref="role" @roleClick="handleRoleClick" /> <Role ref="role" @roleClick="handleRoleClick" />
</el-card> </el-card>
</el-col> </el-col>
@@ -22,7 +22,7 @@
</el-tag> </el-tag>
<el-tag type="warning" v-else size="small">请选择角色</el-tag> <el-tag type="warning" v-else size="small">请选择角色</el-tag>
</template> </template>
<menus ref="menu" @menuClick="handleMenuClick" :role="role" /> <Menus ref="menu" @menuClick="handleMenuClick" :role="role" />
</el-card> </el-card>
</el-col> </el-col>

View File

@@ -212,7 +212,7 @@ import { listSelectDepartments } from "@/api/system/dept";
import { listRoles } from "@/api/system/role"; import { listRoles } from "@/api/system/role";
// 组件依赖 // 组件依赖
import { ElMessage, ElMessageBox, ElTree, ElForm, UploadRequestOptions } from "element-plus"; import { ElMessage, ElMessageBox, ElTree, ElForm, UploadFile } from "element-plus";
import { import {
Search, Search,
Plus, Plus,
@@ -234,7 +234,6 @@ import {
Dialog, Dialog,
UserImportFormData UserImportFormData
} from "@/types"; } from "@/types";
import { assertFile } from "@babel/types";
// DOM元素的引用声明定义 变量名和DOM的ref属性值一致 // DOM元素的引用声明定义 变量名和DOM的ref属性值一致
const deptTreeRef = ref(ElTree); // 部门树 const deptTreeRef = ref(ElTree); // 部门树
@@ -312,7 +311,6 @@ const state = reactive({
const { const {
loading, loading,
single,
multiple, multiple,
queryParams, queryParams,
userList, userList,
@@ -444,7 +442,7 @@ function resetPassword(row: { [key: string]: any }) {
ElMessage.success("修改成功,新密码是:" + value); ElMessage.success("修改成功,新密码是:" + value);
}); });
}) })
.catch(() => { }); .catch(() => {});
} }
/** /**
@@ -489,7 +487,7 @@ function submitForm() {
handleQuery(); handleQuery();
}); });
} else { } else {
addUser(state.formData).then((response: any) => { addUser(state.formData).then(() => {
ElMessage.success("新增用户成功"); ElMessage.success("新增用户成功");
cancel() cancel()
handleQuery(); handleQuery();
@@ -576,8 +574,7 @@ async function showImportDialog() {
} }
function handleExcelChange(file: any, fileList: File[]) { function handleExcelChange(file: UploadFile) {
const fileName = file.name;
if (!/\.(xlsx|xls|XLSX|XLS)$/.test(file.name)) { if (!/\.(xlsx|xls|XLSX|XLS)$/.test(file.name)) {
ElMessage.warning('上传Excel只能为xlsx、xls格式'); ElMessage.warning('上传Excel只能为xlsx、xls格式');
state.excelFile = undefined state.excelFile = undefined

View File

@@ -2,54 +2,35 @@
<div class="app-container"> <div class="app-container">
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> <el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item> <el-form-item>
<el-input <el-input v-model="queryParams.nickName" placeholder="会员昵称" clearable @keyup.enter="handleQuery" />
v-model="queryParams.nickName"
placeholder="会员昵称"
clearable
@keyup.enter="handleQuery"
/>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery" <el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
>搜索</el-button
>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button> <el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-table <el-table v-loading="loading" :data="memberList" border @selection-change="handleSelectionChange">
v-loading="loading"
:data="memberList"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" align="center" /> <el-table-column type="selection" align="center" />
<el-table-column type="expand" width="120" label="会员地址"> <el-table-column type="expand" width="120" label="会员地址">
<template #default="scope"> <template #default="scope">
<el-table :data="scope.row.addressList" size="small" border> <el-table :data="scope.row.addressList" size="small" border>
<el-table-column <el-table-column type="index" label="序号" width="100" align="center" />
type="index"
label="序号"
width="100"
align="center"
/>
<el-table-column align="center" label="收货人" prop="name" /> <el-table-column align="center" label="收货人" prop="name" />
<el-table-column align="center" label="联系方式" prop="mobile" /> <el-table-column align="center" label="联系方式" prop="mobile" />
<el-table-column align="center" label="收货地址"> <el-table-column align="center" label="收货地址">
<template #default="scope"> <template #default="scope">
{{ {{
scope.row.province + scope.row.province +
scope.row.city + scope.row.city +
scope.row.area + scope.row.area +
scope.row.address scope.row.address
}} }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column align="center" label="邮编" prop="zipCode" /> <el-table-column align="center" label="邮编" prop="zipCode" />
<el-table-column align="center" label="是否默认"> <el-table-column align="center" label="是否默认">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.defaulted == 1" type="success" <el-tag v-if="scope.row.defaulted == 1" type="success"></el-tag>
></el-tag
>
<el-tag v-if="scope.row.defaulted == 0" type="info"></el-tag> <el-tag v-if="scope.row.defaulted == 0" type="info"></el-tag>
</template> </template>
</el-table-column> </el-table-column>
@@ -71,10 +52,7 @@
<el-popover placement="right" :width="400" trigger="hover"> <el-popover placement="right" :width="400" trigger="hover">
<img :src="scope.row.avatarUrl" width="400" height="400" /> <img :src="scope.row.avatarUrl" width="400" height="400" />
<template #reference> <template #reference>
<img <img :src="scope.row.avatarUrl" style="max-height: 60px; max-width: 60px" />
:src="scope.row.avatarUrl"
style="max-height: 60px; max-width: 60px"
/>
</template> </template>
</el-popover> </el-popover>
</template> </template>
@@ -98,23 +76,18 @@
</el-table> </el-table>
<!-- 分页工具条 --> <!-- 分页工具条 -->
<pagination <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
v-show="total > 0" @pagination="handleQuery" />
:total="total"
v-model:page="queryParams.pageNum"
v-model:limit="queryParams.pageSize"
@pagination="handleQuery"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { reactive, onMounted, toRefs } from "vue"; import { reactive, onMounted, toRefs } from "vue";
import { ElTable, ElMessage, ElMessageBox } from "element-plus"; import { ElTable } from "element-plus";
import { Search, Plus, Edit, Refresh, Delete } from "@element-plus/icons-vue"; import { Search, Refresh } from "@element-plus/icons-vue";
import { listMemeberPages } from "@/api/ums/member"; import { listMemeberPages } from "@/api/ums/member";
import { MemberQueryParam,MemberItem } from "@/types"; import { MemberQueryParam, MemberItem } from "@/types";
const state = reactive({ const state = reactive({
// 遮罩层 // 遮罩层
@@ -128,17 +101,17 @@ const state = reactive({
total: 0, total: 0,
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10
} as MemberQueryParam, } as MemberQueryParam,
memberList: [] as MemberItem[] memberList: [] as MemberItem[]
}); });
const { loading, ids, single, multiple, queryParams, memberList, total } = const { loading, queryParams, memberList, total } =
toRefs(state); toRefs(state);
function handleQuery() { function handleQuery() {
state.loading = true; state.loading = true;
listMemeberPages(state.queryParams).then(({data}) => { listMemeberPages(state.queryParams).then(({ data }) => {
state.memberList = data.list; state.memberList = data.list;
state.total = data.total; state.total = data.total;
state.loading = false; state.loading = false;
@@ -149,7 +122,7 @@ function resetQuery() {
state.queryParams = { state.queryParams = {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
nickName: undefined, nickName: ''
}; };
handleQuery(); handleQuery();
} }