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
*/
export function listCascadeCategories(queryParams:object) {
export function listCascadeCategories(queryParams?:object) {
return request({
url: '/mall-pms/api/v1/categories/cascade',
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 { AxiosPromise } from 'axios'
@@ -20,7 +20,7 @@ export function listGoodsPages(queryParams: GoodsQueryParam):AxiosPromise<GoodsP
*
* @param id
*/
export function getGoodsFormDetail(id: number) {
export function getGoodsDetail(id: string):AxiosPromise<GoodsDetail> {
return request({
url: '/mall-pms/api/v1/goods/' + id,
method: 'get'

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
import request from "@/utils/request";
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({
url: '/youlai-admin/api/v1/users/_export',
method: 'get',
params:queryParams,
responseType: "arraybuffer"
})
}
@@ -123,7 +124,7 @@ export function exportUser(queryParams: UserQueryParam) {
* @param file
*/
export function importUser(deptId: number, roleIds: string, file: File) {
let formData = new FormData()
const formData = new FormData()
formData.append('file', file)
formData.append('deptId',deptId.toString())
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>
<div class="icon-body">
<el-input
v-model="iconName"
style="position: relative;"
clearable
placeholder="请输入图标名称"
@clear="filterIcons"
@input="filterIcons"
>
<el-input v-model="iconName" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons"
@input="filterIcons">
<template #suffix><i class="el-icon-search el-input__icon" /></template>
</el-input>
<div class="icon-list">
@@ -19,11 +13,11 @@
</div>
</template>
<script setup>
import { ref} from "vue";
<script setup lang="ts">
import { ref } from "vue";
import SvgIcon from '@/components/SvgIcon/index.vue';
let icons = []
const icons = [] as string[]
const modules = import.meta.glob('../../assets/icons/svg/*.svg');
for (const path in modules) {
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)
document.body.click()
}
@@ -61,9 +55,11 @@ defineExpose({
.icon-body {
width: 100%;
padding: 10px;
.icon-list {
height: 200px;
overflow-y: scroll;
div {
height: 30px;
line-height: 30px;
@@ -72,6 +68,7 @@ defineExpose({
width: 33%;
float: left;
}
span {
display: inline-block;
vertical-align: -0.15em;

View File

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

View File

@@ -1,42 +1,41 @@
<template>
<div ref="rightPanel" :class="{show:show}" class="rightPanel-container">
<div class="rightPanel-background"/>
<div ref="rightPanel" :class="{ show: show }" class="rightPanel-container">
<div class="rightPanel-background" />
<div class="rightPanel">
<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"/>
<Setting style="width:1em; height:1em;vertical-align: middle " v-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" />
<Setting style="width:1em; height:1em;vertical-align: middle " v-show="!show" />
</div>
<div class="rightPanel-items">
<slot/>
<slot />
</div>
</div>
</div>
</template>
<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 {Close, Setting} from '@element-plus/icons-vue'
import {ElColorPicker} from "element-plus";
import { Close, Setting } from '@element-plus/icons-vue'
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: {
default: 250,
type: Number
}
})
const theme = computed(() =>setting.theme)
const show = ref(false)
watch(show, (value) => {
if (value) {
addEventClick()

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,28 +1,19 @@
<template>
<div style="border: 1px solid #ccc">
<!-- 工具栏 -->
<Toolbar
:editorId="editorId"
:defaultConfig="toolbarConfig"
style="border-bottom: 1px solid #ccc"
/>
<Toolbar :editor="editorRef" :defaultConfig="toolbarConfig" style="border-bottom: 1px solid #ccc" :mode="mode" />
<!-- 编辑器 -->
<Editor
:editorId="editorId"
:defaultConfig="editorConfig"
:defaultHtml="defaultHtml"
@onChange="handleChange"
style="height: 500px; overflow-y: hidden;"
/>
<Editor :defaultConfig="editorConfig" v-model="defaultHtml" @onChange="handleChange"
style="height: 500px; overflow-y: hidden;" :mode="mode" @onCreated="handleCreated" />
</div>
</template>
<script setup lang="ts">
import {computed, onBeforeUnmount, reactive, toRefs} from 'vue'
import {Editor, Toolbar, getEditor, removeEditor} from '@wangeditor/editor-for-vue'
import { onBeforeUnmount, shallowRef, reactive, toRefs} from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
// API 引用
import {uploadFile} from "@/api/system/file";
import { uploadFile } from "@/api/system/file";
const props = defineProps({
modelValue: {
@@ -33,16 +24,18 @@ const props = defineProps({
const emit = defineEmits(['update:modelValue']);
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
const state = reactive({
editorId: `w-e-${Math.random().toString().slice(-5)}`, //【注意】编辑器 id ,要全局唯一
toolbarConfig: {},
editorConfig: {
placeholder: '请输入内容...',
MENU_CONF: {
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 => {
const url = response.data
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())
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = getEditor(state.editorId)
const editor = editorRef.value
if (editor == null) return
editor.destroy()
removeEditor(state.editorId)
})
</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 SizeSelect from "@/components/SizeSelect/index.vue";
import LangSelect from "@/components/LangSelect/index.vue";
import SvgIcon from "@/components/SvgIcon/index.vue";
// 图标依赖
import { CaretBottom } from "@element-plus/icons-vue";

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,8 +23,6 @@
--el-button-text-color: var(--el-color-white) !important;
--el-button-bg-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-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-border-color: var(--el-color-primary-light-5)!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-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 {
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"
/**
* 商品查询参数类型声明
*/
export interface GoodsQueryParam extends PageQueryParam {
name: stirng | undefined,
categoryId: number | undefined
name?: stirng,
categoryId?: number
}
/**
@@ -49,25 +48,25 @@ export interface SkuItem {
/**
* 商品分页项类型声明
*/
export interface GoodsPageResult extends PageResult<GoodsItem[]> {
}
export type GoodsPageResult = PageResult<GoodsItem[]>
/**
* 商品表单数据类型声明
*/
export interface GoodsFormData {
id: number|undefined,
deptId: number,
username: string,
nickname: string,
password: string,
mobile: string,
email: string,
gender: number,
status: number,
remark: string,
roleIds: number[]
export interface GoodsDetail {
id: string,
name: string,
categoryId: string,
brandId: string,
originPrice: number,
price: number,
picUrl: string,
album: string[],
description: string,
detail: string,
attrList: any[],
specList: any[],
skuList: any[]
}

View File

@@ -4,7 +4,7 @@ import { PageQueryParam, PageResult } from "../base"
* 广告查询参数类型声明
*/
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 {
id: number | undefined;
id?: number;
title: string;
picUrl: 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 {
id: number | undefined;
dictCode:string,
id?: number;
dictCode?:string,
dictName?:string;
name: string;
code: string;
value: string;

View File

@@ -2,8 +2,8 @@
/**
*
*/
export interface MenuQueryParam {
name: String | undefined
export interface MenuQueryParam {
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 {
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 {
keywords: String | undefined,
status: number | undefined,
deptId: number | undefined
keywords: string,
status: number,
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[]
}
/**
* 用户导入表单类型声明
*/
export interface UserImportFormData {
export interface UserImportFormData {
deptId: number,
roleIds: number[]
}

View File

@@ -4,7 +4,7 @@ import { PageQueryParam, PageResult } from "../base"
* 会员查询参数类型声明
*/
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) {
weight = Math.max(Math.min(Number(weight), 1), 0);
let r1 = parseInt(color1.substring(1, 3), 16);
let g1 = parseInt(color1.substring(3, 5), 16);
let b1 = parseInt(color1.substring(5, 7), 16);
let r2 = parseInt(color2.substring(1, 3), 16);
let g2 = parseInt(color2.substring(3, 5), 16);
let b2 = parseInt(color2.substring(5, 7), 16);
let r = Math.round(r1 * (1 - weight) + r2 * weight);
let g = Math.round(g1 * (1 - weight) + g2 * weight);
let b = Math.round(b1 * (1 - weight) + b2 * weight);
const r1 = parseInt(color1.substring(1, 3), 16);
const g1 = parseInt(color1.substring(3, 5), 16);
const b1 = parseInt(color1.substring(5, 7), 16);
const r2 = parseInt(color2.substring(1, 3), 16);
const g2 = parseInt(color2.substring(3, 5), 16);
const b2 = parseInt(color2.substring(5, 7), 16);
const r = Math.round(r1 * (1 - weight) + r2 * weight);
const g = Math.round(g1 * (1 - weight) + g2 * weight);
const b = Math.round(b1 * (1 - weight) + b2 * weight);
const rStr = ("0" + (r || 0).toString(16)).slice(-2);
const gStr = ("0" + (g || 0).toString(16)).slice(-2);
const bStr = ("0" + (b || 0).toString(16)).slice(-2);
return "#" + rStr + gStr + bStr;
};
}

View File

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

View File

@@ -34,7 +34,7 @@ const position = () => {
* @param {number} duration
* @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 change = to - start
const increment = 20

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -118,10 +118,10 @@
<script setup lang="ts">
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 { 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);
@@ -196,14 +196,10 @@ const state = reactive({
const {
loading,
single,
multiple,
queryParams,
orderList,
total,
dialog,
dateRange,
orderDetail,
} = toRefs(state);
function handleQuery() {
@@ -227,9 +223,6 @@ function viewDetail(row: any) {
});
}
function cancel() {
state.dialog.visible = false;
}
onMounted(() => {
handleQuery();

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@
<svg-icon icon-class="menu"/>
商品分类
</template>
<category ref="categoryRef" @categoryClick="handleCategoryClick"/>
<Category ref="categoryRef" @categoryClick="handleCategoryClick"/>
</el-card>
</el-col>
@@ -18,9 +18,9 @@
{{category.name}} 规格属性
</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-col>
</el-row>

View File

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

View File

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

View File

@@ -1,74 +1,41 @@
<template>
<div class="component-container">
<div class="component-container__main">
<el-form
ref="dataFormRef"
:rules="rules"
:model="modelValue"
label-width="120px"
>
<el-form ref="dataFormRef" :rules="rules" :model="goodsInfo" label-width="120px">
<el-form-item label="商品品牌" prop="brandId">
<el-select
v-model="modelValue.brandId"
style="width: 400px"
clearable
>
<el-option
v-for="item in brandOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
<el-select v-model="goodsInfo.brandId" style="width: 400px" clearable>
<el-option v-for="item in brandOptions" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<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 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 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 label="商品简介">
<el-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
v-model="modelValue.description"
/>
<el-input type="textarea" :autosize="{ minRows: 3, maxRows: 6 }" v-model="goodsInfo.description" />
</el-form-item>
<el-form-item label="商品相册">
<el-card
v-for="(item, index) in pictures"
:key="index"
<el-card v-for="(item, index) in pictures" :key="index"
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" />
<div v-if="item.url">
<el-button
type="text"
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
>
<el-button type="text" 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 v-else>
<div v-else>
<!-- 占位 -->
<el-button type="text" />
</div>
@@ -76,21 +43,19 @@
</el-form-item>
<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>
</div>
<div class="component-container__footer">
<el-button @click="handlePrev">上一步选择商品分类</el-button>
<el-button type="primary" @click="handleNext"
>下一步设置商品属性</el-button
>
<el-button type="primary" @click="handleNext">下一步设置商品属性</el-button>
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref, toRefs } from "vue";
import { computed, onMounted, reactive, ref, toRefs } from "vue";
import { ElForm } from "element-plus";
// API 依赖
@@ -100,16 +65,23 @@ import { listBrands } from "@/api/pms/brand";
import Editor from "@/components/WangEditor/index.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 props = defineProps({
modelValue: {
type: Object,
default: {},
},
default: () => { }
}
});
const goodsInfo: any = computed({
get: () => props.modelValue,
set: (value) => {
emit('update:modelValue', value)
}
})
const state = reactive({
brandOptions: [] as Array<any>,
// 商品图册
@@ -131,17 +103,16 @@ const state = reactive({
const { brandOptions, pictures, rules } = toRefs(state);
function loadData() {
listBrands().then(({data}) => {
listBrands().then(({ data }) => {
state.brandOptions = data;
});
const goodsInfo = props.modelValue;
const goodsId = goodsInfo.id;
const goodsId = goodsInfo.value.id;
if (goodsId) {
const mainPicUrl = goodsInfo.picUrl;
const mainPicUrl = goodsInfo.value.picUrl;
if (mainPicUrl) {
state.pictures.filter((item) => item.main)[0].url = mainPicUrl;
}
const subPicUrls = goodsInfo.subPicUrls;
const subPicUrls = goodsInfo.value.subPicUrls;
if (subPicUrls && subPicUrls.length > 0) {
for (let i = 1; i <= subPicUrls.length; i++) {
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)
.map((item) => item.url);
if (mainPicUrl && mainPicUrl.length > 0) {
props.modelValue.picUrl = mainPicUrl[0];
goodsInfo.picUrl = mainPicUrl[0];
}
const subPicUrl = state.pictures
.filter((item) => item.main == false && item.url)
.map((item) => item.url);
if (subPicUrl && subPicUrl.length > 0) {
props.modelValue.subPicUrls = subPicUrl;
goodsInfo.subPicUrls = subPicUrl;
}
emit("next");
}

View File

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

View File

@@ -1,108 +1,77 @@
<template>
<div class="app-container">
<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>
<goods-category
v-show="active==0"
v-model="goods"
v-if="loaded==true"
@prev="prev"
@next="next"
/>
<GoodsCategory v-show="active == 0" v-model="goodsInfo" v-if="loaded == true" @prev="prev" @next="next" />
<GoodsInfo v-show="active == 1" v-model="goodsInfo" v-if="loaded == true" @prev="prev" @next="next" />
<GoodsAttribute v-show="active == 2" v-model="goodsInfo" v-if="loaded == true" @prev="prev" @next="next" />
<GoodsStock v-show="active == 3" v-model="goodsInfo" v-if="loaded == true" @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>
</template>
<script>
<script setup lang="ts">
import { onMounted, reactive, toRefs } from "vue";
import GoodsCategory from "./components/GoodsCategory.vue";
import GoodsInfo from "./components/GoodsInfo.vue";
import GoodsAttribute from "./components/GoodsAttribute.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 {
name: "goods-detail",
components: {GoodsStock, GoodsCategory, GoodsInfo, GoodsAttribute},
props: ['goodsId'],
data() {
return {
loaded: false,
active: 0,
goods: {
id: undefined,
name: undefined,
categoryId: undefined,
brandId: undefined,
originPrice: undefined,
price: undefined,
picUrl: undefined,
album: undefined,
description: undefined,
detail: undefined,
attrList: [],
specList: [],
skuList: []
}
};
},
created() {
this.loadData()
},
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;
}
}
const route = useRoute();
const props = defineProps({
goodsId: {
type: String,
default: () => ''
}
})
const state = reactive({
loaded: false,
active: 0,
goodsInfo: {} as GoodsDetail
});
const { loaded, active, goodsInfo } = toRefs(state)
function loadData() {
const goodsId = route.query.goodsId as string
if (goodsId) {
getGoodsDetail(goodsId).then((response) => {
state.goodsInfo = response.data
state.goodsInfo.originPrice = state.goodsInfo.originPrice / 100
state.goodsInfo.price = state.goodsInfo.price / 100
state.loaded = true
})
} else {
state.loaded = true
}
}
function prev() {
if (state.active-- <= 0) {
state.active = 0;
}
}
function next() {
if (state.active++ >= 3) {
state.active = 0;
}
}
onMounted(() => {
loadData();
});
</script>
<style lang="scss" scoped>

View File

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

View File

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

View File

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

View File

@@ -179,7 +179,6 @@ const state = reactive({
ids: [] as number[],
// 非单个禁用
single: true,
disabled: false,
loading: true,
// 表格树数据
deptList: [] as DeptItem[],
@@ -205,9 +204,7 @@ const state = reactive({
});
const {
ids,
single,
disabled,
loading,
deptList,
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) => {
if (valid) {
if (state.formData.id) {
updateDept(state.formData.id, state.formData).then((res: any) => {
updateDept(state.formData.id, state.formData).then(() => {
ElMessage.success("修改成功");
state.dialog.visible = false;
cancel();
handleQuery();
});
} else {
addDept(state.formData).then(() => {
ElMessage.success("新增成功");
state.dialog.visible = false;
cancel();
handleQuery();
});
}
@@ -345,7 +334,8 @@ function handleDelete(row: any) {
* 取消/关闭弹窗
**/
function cancel() {
resetForm();
state.formData.id = undefined;
dataFormRef.value.resetFields();
state.dialog.visible = false;
}

View File

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

View File

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

View File

@@ -17,11 +17,11 @@
<template #header>
<svg-icon color="#333" icon-class="dict"/>
<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>
</template>
<!-- 字典项组件 -->
<dict-item :dictName="state.dictName" :dictCode='state.dictCode'/>
<dict-item :dictName="dictName" :dictCode='dictCode'/>
</el-card>
</el-col>
</el-row>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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