前端代码从vue2升级到vue3
This commit is contained in:
parent
79b3e3a751
commit
ec2781d166
22
.gitignore
vendored
22
.gitignore
vendored
@ -48,3 +48,25 @@ nbdist/
|
|||||||
!*/build/*.xml
|
!*/build/*.xml
|
||||||
|
|
||||||
.flattened-pom.xml
|
.flattened-pom.xml
|
||||||
|
|
||||||
|
DS_Store
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
**/*.log
|
||||||
|
|
||||||
|
tests/**/coverage/
|
||||||
|
tests/e2e/reports
|
||||||
|
selenium-debug.log
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.local
|
||||||
|
|
||||||
|
package-lock.json
|
||||||
|
yarn.lock
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# 项目相关配置
|
# 项目相关配置
|
||||||
ruoyi:
|
ruoyi:
|
||||||
# 名称
|
# 名称
|
||||||
name: RuoYi-Flex
|
name: Ruoyi-Flex
|
||||||
# 版本
|
# 版本
|
||||||
version: 4.1.6
|
version: 4.1.6
|
||||||
# 版权年份
|
# 版权年份
|
||||||
@ -217,6 +217,7 @@ security:
|
|||||||
- /**/*.html
|
- /**/*.html
|
||||||
- /**/*.css
|
- /**/*.css
|
||||||
- /**/*.js
|
- /**/*.js
|
||||||
|
- /profile/**
|
||||||
# 公共路径
|
# 公共路径
|
||||||
- /favicon.ico
|
- /favicon.ico
|
||||||
- /error
|
- /error
|
||||||
|
@ -10,7 +10,6 @@ import org.springframework.stereotype.Component;
|
|||||||
*
|
*
|
||||||
* @author ruoyi
|
* @author ruoyi
|
||||||
*/
|
*/
|
||||||
@Data
|
|
||||||
@Component
|
@Component
|
||||||
@ConfigurationProperties(prefix = "ruoyi")
|
@ConfigurationProperties(prefix = "ruoyi")
|
||||||
public class RuoYiConfig {
|
public class RuoYiConfig {
|
||||||
@ -43,11 +42,46 @@ public class RuoYiConfig {
|
|||||||
/**
|
/**
|
||||||
* 获取地址开关
|
* 获取地址开关
|
||||||
*/
|
*/
|
||||||
@Getter
|
|
||||||
private static boolean addressEnabled;
|
private static boolean addressEnabled;
|
||||||
|
|
||||||
public void setAddressEnabled(boolean addressEnabled) {
|
public String getName()
|
||||||
RuoYiConfig.addressEnabled = addressEnabled;
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name)
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getVersion()
|
||||||
|
{
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(String version)
|
||||||
|
{
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCopyrightYear()
|
||||||
|
{
|
||||||
|
return copyrightYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCopyrightYear(String copyrightYear)
|
||||||
|
{
|
||||||
|
this.copyrightYear = copyrightYear;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDemoEnabled()
|
||||||
|
{
|
||||||
|
return demoEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDemoEnabled(boolean demoEnabled)
|
||||||
|
{
|
||||||
|
this.demoEnabled = demoEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getProfile()
|
public static String getProfile()
|
||||||
@ -55,6 +89,22 @@ public class RuoYiConfig {
|
|||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setProfile(String profile)
|
||||||
|
{
|
||||||
|
RuoYiConfig.profile = profile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAddressEnabled()
|
||||||
|
{
|
||||||
|
return addressEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAddressEnabled(boolean addressEnabled)
|
||||||
|
{
|
||||||
|
RuoYiConfig.addressEnabled = addressEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取导入上传路径
|
* 获取导入上传路径
|
||||||
*/
|
*/
|
||||||
|
@ -17,6 +17,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties
|
|||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 权限安全配置
|
* 权限安全配置
|
||||||
*
|
*
|
||||||
@ -38,12 +40,32 @@ public class SecurityConfig implements WebMvcConfigurer {
|
|||||||
public void addInterceptors(InterceptorRegistry registry) {
|
public void addInterceptors(InterceptorRegistry registry) {
|
||||||
// 注册路由拦截器,自定义验证规则
|
// 注册路由拦截器,自定义验证规则
|
||||||
registry.addInterceptor(new SaInterceptor(handler -> {
|
registry.addInterceptor(new SaInterceptor(handler -> {
|
||||||
AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
|
//AllUrlHandler allUrlHandler = SpringUtils.getBean(AllUrlHandler.class);
|
||||||
// 登录验证 -- 排除多个路径
|
// 登录验证 -- 排除多个路径
|
||||||
SaRouter
|
SaRouter
|
||||||
// 获取所有的
|
// 获取所有的
|
||||||
.match(allUrlHandler.getUrls())
|
//.match(allUrlHandler.getUrls()) // 拦截的 path 列表
|
||||||
// 对未排除的路径进行检查
|
.match("/**")
|
||||||
|
.notMatch(
|
||||||
|
Arrays.asList(
|
||||||
|
"/*.html",
|
||||||
|
"/**/*.html",
|
||||||
|
"/**/*.css",
|
||||||
|
"/**/*.js",
|
||||||
|
"/**/*.css",
|
||||||
|
"/**/*.js",
|
||||||
|
"/profile/**",
|
||||||
|
"/favicon.ico",
|
||||||
|
"/error",
|
||||||
|
"/*/api-docs",
|
||||||
|
"/*/api-docs/**",
|
||||||
|
"/actuator",
|
||||||
|
"/actuator/**",
|
||||||
|
"/login",
|
||||||
|
"/register",
|
||||||
|
"/captchaImage",
|
||||||
|
"/captcha/get",
|
||||||
|
"/captcha/check"))
|
||||||
.check(() -> {
|
.check(() -> {
|
||||||
// 检查是否登录 是否有token
|
// 检查是否登录 是否有token
|
||||||
StpUtil.checkLogin();
|
StpUtil.checkLogin();
|
||||||
@ -51,9 +73,9 @@ public class SecurityConfig implements WebMvcConfigurer {
|
|||||||
//TODO :以后完善多平台登录校验clientID功能
|
//TODO :以后完善多平台登录校验clientID功能
|
||||||
|
|
||||||
});
|
});
|
||||||
})).addPathPatterns("/**")
|
})).addPathPatterns("/**");
|
||||||
// 排除不需要拦截的路径
|
// 排除不需要拦截的路径
|
||||||
.excludePathPatterns(securityProperties.getExcludes());
|
//.excludePathPatterns(securityProperties.getExcludes());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
com.ruoyi.common.security.config.SaTokenConfig
|
com.ruoyi.common.security.config.SaTokenConfig
|
||||||
|
com.ruoyi.common.security.config.SecurityConfig
|
||||||
com.ruoyi.common.security.handler.GlobalExceptionHandler
|
com.ruoyi.common.security.handler.GlobalExceptionHandler
|
||||||
com.ruoyi.common.security.handler.AllUrlHandler
|
com.ruoyi.common.security.handler.AllUrlHandler
|
||||||
com.ruoyi.common.security.config.SecurityConfig
|
|
@ -26,7 +26,7 @@ oms.http.port=10010
|
|||||||
oms.table-prefix=pj_
|
oms.table-prefix=pj_
|
||||||
|
|
||||||
# Actuator 监控端点的配置项
|
# Actuator 监控端点的配置项
|
||||||
spring.application.name: ruoyi-powerjob-server
|
spring.application.name: Ruoyi-Powerjob-Server
|
||||||
management.endpoints.web.exposure.include=*
|
management.endpoints.web.exposure.include=*
|
||||||
management.endpoint.health.show-details=ALWAYS
|
management.endpoint.health.show-details=ALWAYS
|
||||||
management.endpoint.logfile.external-file=./logs/ruoyi-powerjob-server.log
|
management.endpoint.logfile.external-file=./logs/ruoyi-powerjob-server.log
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||||
#foreach($column in $columns)
|
#foreach($column in $columns)
|
||||||
#if($column.query)
|
#if($column.query)
|
||||||
#set($dictType=$column.dictType)
|
#set($dictType=$column.dictType)
|
||||||
@ -17,14 +17,14 @@
|
|||||||
v-model="queryParams.${column.javaField}"
|
v-model="queryParams.${column.javaField}"
|
||||||
placeholder="请输入${comment}"
|
placeholder="请输入${comment}"
|
||||||
clearable
|
clearable
|
||||||
@keyup.enter.native="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
|
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
<el-form-item label="${comment}" prop="${column.javaField}">
|
||||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
|
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in dict.type.${dictType}"
|
v-for="dict in ${dictType}"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
@ -42,16 +42,15 @@
|
|||||||
<el-date-picker clearable
|
<el-date-picker clearable
|
||||||
v-model="queryParams.${column.javaField}"
|
v-model="queryParams.${column.javaField}"
|
||||||
type="date"
|
type="date"
|
||||||
value-format="yyyy-MM-dd"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="选择${comment}">
|
placeholder="选择${comment}">
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
<el-form-item label="${comment}">
|
<el-form-item label="${comment}" style="width: 308px">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="daterange${AttrName}"
|
v-model="daterange${AttrName}"
|
||||||
style="width: 240px"
|
value-format="YYYY-MM-DD"
|
||||||
value-format="yyyy-MM-dd"
|
|
||||||
type="daterange"
|
type="daterange"
|
||||||
range-separator="-"
|
range-separator="-"
|
||||||
start-placeholder="开始日期"
|
start-placeholder="开始日期"
|
||||||
@ -62,8 +61,8 @@
|
|||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
@ -72,8 +71,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-plus"
|
icon="Plus"
|
||||||
size="mini"
|
|
||||||
@click="handleAdd"
|
@click="handleAdd"
|
||||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
v-hasPermi="['${moduleName}:${businessName}:add']"
|
||||||
>新增</el-button>
|
>新增</el-button>
|
||||||
@ -82,12 +80,11 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="info"
|
type="info"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-sort"
|
icon="Sort"
|
||||||
size="mini"
|
|
||||||
@click="toggleExpandAll"
|
@click="toggleExpandAll"
|
||||||
>展开/折叠</el-button>
|
>展开/折叠</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
@ -109,23 +106,23 @@
|
|||||||
#if($column.pk)
|
#if($column.pk)
|
||||||
#elseif($column.list && $column.htmlType == "datetime")
|
#elseif($column.list && $column.htmlType == "datetime")
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
|
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
#elseif($column.list && $column.htmlType == "imageUpload")
|
#elseif($column.list && $column.htmlType == "imageUpload")
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
|
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
|
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
#elseif($column.list && "" != $column.dictType)
|
#elseif($column.list && "" != $column.dictType)
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}">
|
<el-table-column label="${comment}" align="center" prop="${javaField}">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
#if($column.htmlType == "checkbox")
|
#if($column.htmlType == "checkbox")
|
||||||
<dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
|
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
|
||||||
#else
|
#else
|
||||||
<dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField}"/>
|
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField}"/>
|
||||||
#end
|
#end
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@ -138,35 +135,17 @@
|
|||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
||||||
size="mini"
|
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
|
||||||
type="text"
|
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
||||||
icon="el-icon-edit"
|
|
||||||
@click="handleUpdate(scope.row)"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
|
||||||
>修改</el-button>
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="text"
|
|
||||||
icon="el-icon-plus"
|
|
||||||
@click="handleAdd(scope.row)"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
|
||||||
>新增</el-button>
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="text"
|
|
||||||
icon="el-icon-delete"
|
|
||||||
@click="handleDelete(scope.row)"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
|
||||||
>删除</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<!-- 添加或修改${functionName}对话框 -->
|
<!-- 添加或修改${functionName}对话框 -->
|
||||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
<el-form ref="${businessName}Ref" :model="form" :rules="rules" label-width="80px">
|
||||||
#foreach($column in $columns)
|
#foreach($column in $columns)
|
||||||
#set($field=$column.javaField)
|
#set($field=$column.javaField)
|
||||||
#if($column.insert && !$column.pk)
|
#if($column.insert && !$column.pk)
|
||||||
@ -180,7 +159,14 @@
|
|||||||
#set($dictType=$column.dictType)
|
#set($dictType=$column.dictType)
|
||||||
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
|
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
|
||||||
<el-form-item label="${comment}" prop="${treeParentCode}">
|
<el-form-item label="${comment}" prop="${treeParentCode}">
|
||||||
<treeselect v-model="form.${treeParentCode}" :options="${businessName}Options" :normalizer="normalizer" placeholder="请选择${comment}" />
|
<el-tree-select
|
||||||
|
v-model="form.${treeParentCode}"
|
||||||
|
:data="${businessName}Options"
|
||||||
|
:props="{ value: '${treeCode}', label: '${treeName}', children: 'children' }"
|
||||||
|
value-key="${treeCode}"
|
||||||
|
placeholder="请选择${comment}"
|
||||||
|
check-strictly
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
#elseif($column.htmlType == "input")
|
#elseif($column.htmlType == "input")
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
<el-form-item label="${comment}" prop="${field}">
|
||||||
@ -202,7 +188,7 @@
|
|||||||
<el-form-item label="${comment}" prop="${field}">
|
<el-form-item label="${comment}" prop="${field}">
|
||||||
<el-select v-model="form.${field}" placeholder="请选择${comment}">
|
<el-select v-model="form.${field}" placeholder="请选择${comment}">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in dict.type.${dictType}"
|
v-for="dict in ${dictType}"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
||||||
@ -223,7 +209,7 @@
|
|||||||
<el-form-item label="${comment}" prop="${field}">
|
<el-form-item label="${comment}" prop="${field}">
|
||||||
<el-checkbox-group v-model="form.${field}">
|
<el-checkbox-group v-model="form.${field}">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-for="dict in dict.type.${dictType}"
|
v-for="dict in ${dictType}"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.value">
|
:label="dict.value">
|
||||||
{{dict.label}}
|
{{dict.label}}
|
||||||
@ -240,7 +226,7 @@
|
|||||||
<el-form-item label="${comment}" prop="${field}">
|
<el-form-item label="${comment}" prop="${field}">
|
||||||
<el-radio-group v-model="form.${field}">
|
<el-radio-group v-model="form.${field}">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in dict.type.${dictType}"
|
v-for="dict in ${dictType}"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
||||||
:label="parseInt(dict.value)"
|
:label="parseInt(dict.value)"
|
||||||
@ -261,7 +247,7 @@
|
|||||||
<el-date-picker clearable
|
<el-date-picker clearable
|
||||||
v-model="form.${field}"
|
v-model="form.${field}"
|
||||||
type="date"
|
type="date"
|
||||||
value-format="yyyy-MM-dd"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="选择${comment}">
|
placeholder="选择${comment}">
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -274,65 +260,59 @@
|
|||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<template #footer>
|
||||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
<div class="dialog-footer">
|
||||||
<el-button @click="cancel">取 消</el-button>
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
</div>
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup name="${BusinessName}">
|
||||||
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
|
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
|
||||||
import Treeselect from "@riophae/vue-treeselect";
|
|
||||||
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
|
|
||||||
|
|
||||||
export default {
|
const { proxy } = getCurrentInstance();
|
||||||
name: "${BusinessName}",
|
|
||||||
#if(${dicts} != '')
|
#if(${dicts} != '')
|
||||||
dicts: [${dicts}],
|
#set($dictsNoSymbol=$dicts.replace("'", ""))
|
||||||
|
const { ${dictsNoSymbol} } = proxy.useDict(${dicts});
|
||||||
#end
|
#end
|
||||||
components: {
|
|
||||||
Treeselect
|
// 树表表格数据
|
||||||
},
|
const ${businessName}List = ref([]);
|
||||||
data() {
|
// 树选项
|
||||||
return {
|
const ${businessName}Options = ref([]);
|
||||||
// 遮罩层
|
// 是否显示弹出层
|
||||||
loading: true,
|
const open = ref(false);
|
||||||
// 显示搜索条件
|
// 遮罩层
|
||||||
showSearch: true,
|
const loading = ref(true);
|
||||||
// ${functionName}表格数据
|
// 显示搜索条件
|
||||||
${businessName}List: [],
|
const showSearch = ref(true);
|
||||||
// ${functionName}树选项
|
// 弹出层标题
|
||||||
${businessName}Options: [],
|
const title = ref("");
|
||||||
// 弹出层标题
|
// 是否展开,默认全部展开
|
||||||
title: "",
|
const isExpandAll = ref(true);
|
||||||
// 是否显示弹出层
|
// 重新渲染表格状态
|
||||||
open: false,
|
const refreshTable = ref(true);
|
||||||
// 是否展开,默认全部展开
|
|
||||||
isExpandAll: true,
|
|
||||||
// 重新渲染表格状态
|
|
||||||
refreshTable: true,
|
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||||
// $comment时间范围
|
const daterange${AttrName} = ref([]);
|
||||||
daterange${AttrName}: [],
|
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
// 查询参数
|
|
||||||
queryParams: {
|
const data = reactive({
|
||||||
#foreach ($column in $columns)
|
form: {},
|
||||||
|
queryParams: {
|
||||||
|
#foreach ($column in $columns)
|
||||||
#if($column.query)
|
#if($column.query)
|
||||||
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
},
|
},
|
||||||
// 表单参数
|
rules: {
|
||||||
form: {},
|
#foreach ($column in $columns)
|
||||||
// 表单校验
|
|
||||||
rules: {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.required)
|
#if($column.required)
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
||||||
#if($parentheseIndex != -1)
|
#if($parentheseIndex != -1)
|
||||||
@ -340,166 +320,157 @@ export default {
|
|||||||
#else
|
#else
|
||||||
#set($comment=$column.columnComment)
|
#set($comment=$column.columnComment)
|
||||||
#end
|
#end
|
||||||
$column.javaField: [
|
$column.javaField: [
|
||||||
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
|
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
|
||||||
]#if($foreach.count != $columns.size()),#end
|
]#if($foreach.count != $columns.size()),#end
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
},
|
|
||||||
created() {
|
const { queryParams, form, rules } = toRefs(data);
|
||||||
this.getList();
|
|
||||||
},
|
/** 查询${functionName}列表 */
|
||||||
methods: {
|
function getList() {
|
||||||
/** 查询${functionName}列表 */
|
loading.value = true;
|
||||||
getList() {
|
|
||||||
this.loading = true;
|
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
this.queryParams.params = {};
|
queryParams.value.params = {};
|
||||||
#break
|
#break
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||||
if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
|
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
|
||||||
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
|
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0];
|
||||||
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
|
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1];
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
list${BusinessName}(this.queryParams).then(response => {
|
list${BusinessName}(queryParams.value).then(response => {
|
||||||
this.${businessName}List = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
|
${businessName}List.value = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
|
||||||
this.loading = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
/** 转换${functionName}数据结构 */
|
|
||||||
normalizer(node) {
|
// 取消按钮
|
||||||
if (node.children && !node.children.length) {
|
function cancel() {
|
||||||
delete node.children;
|
open.value = false;
|
||||||
}
|
reset();
|
||||||
return {
|
}
|
||||||
id: node.${treeCode},
|
|
||||||
label: node.${treeName},
|
// 表单重置
|
||||||
children: node.children
|
function reset() {
|
||||||
};
|
form.value = {
|
||||||
},
|
|
||||||
/** 查询${functionName}下拉树结构 */
|
|
||||||
getTreeselect() {
|
|
||||||
list${BusinessName}().then(response => {
|
|
||||||
this.${businessName}Options = [];
|
|
||||||
const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
|
|
||||||
data.children = this.handleTree(response.data, "${treeCode}", "${treeParentCode}");
|
|
||||||
this.${businessName}Options.push(data);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
// 取消按钮
|
|
||||||
cancel() {
|
|
||||||
this.open = false;
|
|
||||||
this.reset();
|
|
||||||
},
|
|
||||||
// 表单重置
|
|
||||||
reset() {
|
|
||||||
this.form = {
|
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "checkbox")
|
#if($column.htmlType == "checkbox")
|
||||||
$column.javaField: []#if($foreach.count != $columns.size()),#end
|
$column.javaField: []#if($foreach.count != $columns.size()),#end
|
||||||
#else
|
#else
|
||||||
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
};
|
};
|
||||||
this.resetForm("form");
|
proxy.resetForm("${businessName}Ref");
|
||||||
},
|
}
|
||||||
/** 搜索按钮操作 */
|
|
||||||
handleQuery() {
|
/** 搜索按钮操作 */
|
||||||
this.getList();
|
function handleQuery() {
|
||||||
},
|
getList();
|
||||||
/** 重置按钮操作 */
|
}
|
||||||
resetQuery() {
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
function resetQuery() {
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||||
this.daterange${AttrName} = [];
|
daterange${AttrName}.value = [];
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
this.resetForm("queryForm");
|
proxy.resetForm("queryRef");
|
||||||
this.handleQuery();
|
handleQuery();
|
||||||
},
|
}
|
||||||
/** 新增按钮操作 */
|
|
||||||
handleAdd(row) {
|
/** 新增按钮操作 */
|
||||||
this.reset();
|
async function handleAdd(row) {
|
||||||
this.getTreeselect();
|
reset();
|
||||||
if (row != null && row.${treeCode}) {
|
await list${BusinessName}().then(response => {
|
||||||
this.form.${treeParentCode} = row.${treeCode};
|
${businessName}Options.value = proxy.handleTree(response.data, "${treeCode}");
|
||||||
} else {
|
});
|
||||||
this.form.${treeParentCode} = 0;
|
if (row != null && row.${treeCode}) {
|
||||||
}
|
form.value.${treeParentCode} = row.${treeCode};
|
||||||
this.open = true;
|
} else {
|
||||||
this.title = "添加${functionName}";
|
form.value.${treeParentCode} = 0;
|
||||||
},
|
|
||||||
/** 展开/折叠操作 */
|
|
||||||
toggleExpandAll() {
|
|
||||||
this.refreshTable = false;
|
|
||||||
this.isExpandAll = !this.isExpandAll;
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.refreshTable = true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/** 修改按钮操作 */
|
|
||||||
handleUpdate(row) {
|
|
||||||
this.reset();
|
|
||||||
this.getTreeselect();
|
|
||||||
if (row != null) {
|
|
||||||
this.form.${treeParentCode} = row.${treeCode};
|
|
||||||
}
|
|
||||||
get${BusinessName}(row.${pkColumn.javaField}).then(response => {
|
|
||||||
this.form = response.data;
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
this.form.$column.javaField = this.form.${column.javaField}.split(",");
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
this.open = true;
|
|
||||||
this.title = "修改${functionName}";
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/** 提交按钮 */
|
|
||||||
submitForm() {
|
|
||||||
this.#[[$]]#refs["form"].validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
this.form.$column.javaField = this.form.${column.javaField}.join(",");
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
if (this.form.${pkColumn.javaField} != null) {
|
|
||||||
update${BusinessName}(this.form).then(response => {
|
|
||||||
this.#[[$modal]]#.msgSuccess("修改成功");
|
|
||||||
this.open = false;
|
|
||||||
this.getList();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
add${BusinessName}(this.form).then(response => {
|
|
||||||
this.#[[$modal]]#.msgSuccess("新增成功");
|
|
||||||
this.open = false;
|
|
||||||
this.getList();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
handleDelete(row) {
|
|
||||||
this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() {
|
|
||||||
return del${BusinessName}(row.${pkColumn.javaField});
|
|
||||||
}).then(() => {
|
|
||||||
this.getList();
|
|
||||||
this.#[[$modal]]#.msgSuccess("删除成功");
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
open.value = true;
|
||||||
|
title.value = "添加${functionName}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 展开/折叠操作 */
|
||||||
|
function toggleExpandAll() {
|
||||||
|
refreshTable.value = false;
|
||||||
|
isExpandAll.value = !isExpandAll.value;
|
||||||
|
nextTick(() => {
|
||||||
|
refreshTable.value = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改按钮操作 */
|
||||||
|
async function handleUpdate(row) {
|
||||||
|
reset();
|
||||||
|
await listProduct().then(response => {
|
||||||
|
${businessName}Options.value = proxy.handleTree(response.data, "${treeCode}");
|
||||||
|
});
|
||||||
|
if (row != null) {
|
||||||
|
form.value.${treeParentCode} = row.${treeCode};
|
||||||
|
}
|
||||||
|
get${BusinessName}(row.${pkColumn.javaField}).then(response => {
|
||||||
|
form.value = response.data;
|
||||||
|
#foreach ($column in $columns)
|
||||||
|
#if($column.htmlType == "checkbox")
|
||||||
|
form.value.$column.javaField = form.value.${column.javaField}.split(",");
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
open.value = true;
|
||||||
|
title.value = "修改${functionName}";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交按钮 */
|
||||||
|
function submitForm() {
|
||||||
|
proxy.#[[$]]#refs["${businessName}Ref"].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
#foreach ($column in $columns)
|
||||||
|
#if($column.htmlType == "checkbox")
|
||||||
|
form.value.$column.javaField = form.value.${column.javaField}.join(",");
|
||||||
|
#end
|
||||||
|
#end
|
||||||
|
if (form.value.${pkColumn.javaField} != null) {
|
||||||
|
update${BusinessName}(form.value).then(response => {
|
||||||
|
proxy.#[[$modal]]#.msgSuccess("修改成功");
|
||||||
|
open.value = false;
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
add${BusinessName}(form.value).then(response => {
|
||||||
|
proxy.#[[$modal]]#.msgSuccess("新增成功");
|
||||||
|
open.value = false;
|
||||||
|
getList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除按钮操作 */
|
||||||
|
function handleDelete(row) {
|
||||||
|
proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() {
|
||||||
|
return del${BusinessName}(row.${pkColumn.javaField});
|
||||||
|
}).then(() => {
|
||||||
|
getList();
|
||||||
|
proxy.#[[$modal]]#.msgSuccess("删除成功");
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
getList();
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||||
#foreach($column in $columns)
|
#foreach($column in $columns)
|
||||||
#if($column.query)
|
#if($column.query)
|
||||||
#set($dictType=$column.dictType)
|
#set($dictType=$column.dictType)
|
||||||
@ -17,14 +17,14 @@
|
|||||||
v-model="queryParams.${column.javaField}"
|
v-model="queryParams.${column.javaField}"
|
||||||
placeholder="请输入${comment}"
|
placeholder="请输入${comment}"
|
||||||
clearable
|
clearable
|
||||||
@keyup.enter.native="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
|
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
<el-form-item label="${comment}" prop="${column.javaField}">
|
||||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
|
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in dict.type.${dictType}"
|
v-for="dict in ${dictType}"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
@ -42,16 +42,15 @@
|
|||||||
<el-date-picker clearable
|
<el-date-picker clearable
|
||||||
v-model="queryParams.${column.javaField}"
|
v-model="queryParams.${column.javaField}"
|
||||||
type="date"
|
type="date"
|
||||||
value-format="yyyy-MM-dd"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="请选择${comment}">
|
placeholder="请选择${comment}">
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
<el-form-item label="${comment}">
|
<el-form-item label="${comment}" style="width: 308px">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="daterange${AttrName}"
|
v-model="daterange${AttrName}"
|
||||||
style="width: 240px"
|
value-format="YYYY-MM-DD"
|
||||||
value-format="yyyy-MM-dd"
|
|
||||||
type="daterange"
|
type="daterange"
|
||||||
range-separator="-"
|
range-separator="-"
|
||||||
start-placeholder="开始日期"
|
start-placeholder="开始日期"
|
||||||
@ -62,8 +61,8 @@
|
|||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
@ -72,8 +71,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-plus"
|
icon="Plus"
|
||||||
size="mini"
|
|
||||||
@click="handleAdd"
|
@click="handleAdd"
|
||||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
v-hasPermi="['${moduleName}:${businessName}:add']"
|
||||||
>新增</el-button>
|
>新增</el-button>
|
||||||
@ -82,8 +80,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="success"
|
type="success"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-edit"
|
icon="Edit"
|
||||||
size="mini"
|
|
||||||
:disabled="single"
|
:disabled="single"
|
||||||
@click="handleUpdate"
|
@click="handleUpdate"
|
||||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
||||||
@ -93,8 +90,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="danger"
|
type="danger"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-delete"
|
icon="Delete"
|
||||||
size="mini"
|
|
||||||
:disabled="multiple"
|
:disabled="multiple"
|
||||||
@click="handleDelete"
|
@click="handleDelete"
|
||||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
||||||
@ -104,13 +100,12 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="warning"
|
type="warning"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-download"
|
icon="Download"
|
||||||
size="mini"
|
|
||||||
@click="handleExport"
|
@click="handleExport"
|
||||||
v-hasPermi="['${moduleName}:${businessName}:export']"
|
v-hasPermi="['${moduleName}:${businessName}:export']"
|
||||||
>导出</el-button>
|
>导出</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange">
|
||||||
@ -127,23 +122,23 @@
|
|||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" />
|
<el-table-column label="${comment}" align="center" prop="${javaField}" />
|
||||||
#elseif($column.list && $column.htmlType == "datetime")
|
#elseif($column.list && $column.htmlType == "datetime")
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
|
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
#elseif($column.list && $column.htmlType == "imageUpload")
|
#elseif($column.list && $column.htmlType == "imageUpload")
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
|
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
|
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
#elseif($column.list && "" != $column.dictType)
|
#elseif($column.list && "" != $column.dictType)
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}">
|
<el-table-column label="${comment}" align="center" prop="${javaField}">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
#if($column.htmlType == "checkbox")
|
#if($column.htmlType == "checkbox")
|
||||||
<dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
|
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
|
||||||
#else
|
#else
|
||||||
<dict-tag :options="dict.type.${column.dictType}" :value="scope.row.${javaField}"/>
|
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField}"/>
|
||||||
#end
|
#end
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@ -152,21 +147,9 @@
|
|||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
||||||
size="mini"
|
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
||||||
type="text"
|
|
||||||
icon="el-icon-edit"
|
|
||||||
@click="handleUpdate(scope.row)"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
|
||||||
>修改</el-button>
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="text"
|
|
||||||
icon="el-icon-delete"
|
|
||||||
@click="handleDelete(scope.row)"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
|
||||||
>删除</el-button>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@ -174,14 +157,14 @@
|
|||||||
<pagination
|
<pagination
|
||||||
v-show="total>0"
|
v-show="total>0"
|
||||||
:total="total"
|
:total="total"
|
||||||
:page.sync="queryParams.pageNum"
|
v-model:page="queryParams.pageNum"
|
||||||
:limit.sync="queryParams.pageSize"
|
v-model:limit="queryParams.pageSize"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 添加或修改${functionName}对话框 -->
|
<!-- 添加或修改${functionName}对话框 -->
|
||||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
<el-form ref="${businessName}Ref" :model="form" :rules="rules" label-width="80px">
|
||||||
#foreach($column in $columns)
|
#foreach($column in $columns)
|
||||||
#set($field=$column.javaField)
|
#set($field=$column.javaField)
|
||||||
#if($column.insert && !$column.pk)
|
#if($column.insert && !$column.pk)
|
||||||
@ -213,7 +196,7 @@
|
|||||||
<el-form-item label="${comment}" prop="${field}">
|
<el-form-item label="${comment}" prop="${field}">
|
||||||
<el-select v-model="form.${field}" placeholder="请选择${comment}">
|
<el-select v-model="form.${field}" placeholder="请选择${comment}">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in dict.type.${dictType}"
|
v-for="dict in ${dictType}"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
||||||
@ -234,7 +217,7 @@
|
|||||||
<el-form-item label="${comment}" prop="${field}">
|
<el-form-item label="${comment}" prop="${field}">
|
||||||
<el-checkbox-group v-model="form.${field}">
|
<el-checkbox-group v-model="form.${field}">
|
||||||
<el-checkbox
|
<el-checkbox
|
||||||
v-for="dict in dict.type.${dictType}"
|
v-for="dict in ${dictType}"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.value">
|
:label="dict.value">
|
||||||
{{dict.label}}
|
{{dict.label}}
|
||||||
@ -251,7 +234,7 @@
|
|||||||
<el-form-item label="${comment}" prop="${field}">
|
<el-form-item label="${comment}" prop="${field}">
|
||||||
<el-radio-group v-model="form.${field}">
|
<el-radio-group v-model="form.${field}">
|
||||||
<el-radio
|
<el-radio
|
||||||
v-for="dict in dict.type.${dictType}"
|
v-for="dict in ${dictType}"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
||||||
:label="parseInt(dict.value)"
|
:label="parseInt(dict.value)"
|
||||||
@ -272,7 +255,7 @@
|
|||||||
<el-date-picker clearable
|
<el-date-picker clearable
|
||||||
v-model="form.${field}"
|
v-model="form.${field}"
|
||||||
type="date"
|
type="date"
|
||||||
value-format="yyyy-MM-dd"
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="请选择${comment}">
|
placeholder="请选择${comment}">
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -288,10 +271,10 @@
|
|||||||
<el-divider content-position="center">${subTable.functionName}信息</el-divider>
|
<el-divider content-position="center">${subTable.functionName}信息</el-divider>
|
||||||
<el-row :gutter="10" class="mb8">
|
<el-row :gutter="10" class="mb8">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd${subClassName}">添加</el-button>
|
<el-button type="primary" icon="Plus" @click="handleAdd${subClassName}">添加</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="danger" icon="el-icon-delete" size="mini" @click="handleDelete${subClassName}">删除</el-button>
|
<el-button type="danger" icon="Delete" @click="handleDelete${subClassName}">删除</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}">
|
<el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}">
|
||||||
@ -308,22 +291,27 @@
|
|||||||
#if($column.pk || $javaField == ${subTableFkclassName})
|
#if($column.pk || $javaField == ${subTableFkclassName})
|
||||||
#elseif($column.list && $column.htmlType == "input")
|
#elseif($column.list && $column.htmlType == "input")
|
||||||
<el-table-column label="$comment" prop="${javaField}" width="150">
|
<el-table-column label="$comment" prop="${javaField}" width="150">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.$javaField" placeholder="请输入$comment" />
|
<el-input v-model="scope.row.$javaField" placeholder="请输入$comment" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
#elseif($column.list && $column.htmlType == "datetime")
|
#elseif($column.list && $column.htmlType == "datetime")
|
||||||
<el-table-column label="$comment" prop="${javaField}" width="240">
|
<el-table-column label="$comment" prop="${javaField}" width="240">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-date-picker clearable v-model="scope.row.$javaField" type="date" value-format="yyyy-MM-dd" placeholder="请选择$comment" />
|
<el-date-picker clearable
|
||||||
|
v-model="scope.row.$javaField"
|
||||||
|
type="date"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
placeholder="请选择$comment">
|
||||||
|
</el-date-picker>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
|
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
|
||||||
<el-table-column label="$comment" prop="${javaField}" width="150">
|
<el-table-column label="$comment" prop="${javaField}" width="150">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
|
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in dict.type.$column.dictType"
|
v-for="dict in $column.dictType"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
@ -333,7 +321,7 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
|
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
|
||||||
<el-table-column label="$comment" prop="${javaField}" width="150">
|
<el-table-column label="$comment" prop="${javaField}" width="150">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
|
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
|
||||||
<el-option label="请选择字典生成" value="" />
|
<el-option label="请选择字典生成" value="" />
|
||||||
</el-select>
|
</el-select>
|
||||||
@ -344,72 +332,71 @@
|
|||||||
</el-table>
|
</el-table>
|
||||||
#end
|
#end
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<template #footer>
|
||||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
<div class="dialog-footer">
|
||||||
<el-button @click="cancel">取 消</el-button>
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
</div>
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup name="${BusinessName}">
|
||||||
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
|
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
|
||||||
|
|
||||||
export default {
|
const { proxy } = getCurrentInstance();
|
||||||
name: "${BusinessName}",
|
|
||||||
#if(${dicts} != '')
|
#if(${dicts} != '')
|
||||||
dicts: [${dicts}],
|
#set($dictsNoSymbol=$dicts.replace("'", ""))
|
||||||
|
const { ${dictsNoSymbol} } = proxy.useDict(${dicts});
|
||||||
#end
|
#end
|
||||||
data() {
|
|
||||||
return {
|
// 表格数据
|
||||||
// 遮罩层
|
const ${businessName}List = ref([]);
|
||||||
loading: true,
|
|
||||||
// 选中数组
|
|
||||||
ids: [],
|
|
||||||
#if($table.sub)
|
#if($table.sub)
|
||||||
// 子表选中数据
|
// 子表格数据
|
||||||
checked${subClassName}: [],
|
const ${subclassName}List = ref([]);
|
||||||
#end
|
#end
|
||||||
// 非单个禁用
|
// 是否显示弹出层
|
||||||
single: true,
|
const open = ref(false);
|
||||||
// 非多个禁用
|
// 遮罩层
|
||||||
multiple: true,
|
const loading = ref(true);
|
||||||
// 显示搜索条件
|
// 显示搜索条件
|
||||||
showSearch: true,
|
const showSearch = ref(true);
|
||||||
// 总条数
|
// 选中数组
|
||||||
total: 0,
|
const ids = ref([]);
|
||||||
// ${functionName}表格数据
|
|
||||||
${businessName}List: [],
|
|
||||||
#if($table.sub)
|
#if($table.sub)
|
||||||
// ${subTable.functionName}表格数据
|
// 子表选中数据
|
||||||
${subclassName}List: [],
|
const checked${subClassName} = ref([]);
|
||||||
#end
|
#end
|
||||||
// 弹出层标题
|
// 非单个禁用
|
||||||
title: "",
|
const single = ref(true);
|
||||||
// 是否显示弹出层
|
// 非多个禁用
|
||||||
open: false,
|
const multiple = ref(true);
|
||||||
|
// 总条数
|
||||||
|
const total = ref(0);
|
||||||
|
// 弹出层标题
|
||||||
|
const title = ref("");
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||||
// $comment时间范围
|
const daterange${AttrName} = ref([]);
|
||||||
daterange${AttrName}: [],
|
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
// 查询参数
|
|
||||||
queryParams: {
|
const data = reactive({
|
||||||
pageNum: 1,
|
form: {},
|
||||||
pageSize: 10,
|
queryParams: {
|
||||||
#foreach ($column in $columns)
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
#foreach ($column in $columns)
|
||||||
#if($column.query)
|
#if($column.query)
|
||||||
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
},
|
},
|
||||||
// 表单参数
|
rules: {
|
||||||
form: {},
|
#foreach ($column in $columns)
|
||||||
// 表单校验
|
|
||||||
rules: {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.required)
|
#if($column.required)
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
||||||
#if($parentheseIndex != -1)
|
#if($parentheseIndex != -1)
|
||||||
@ -417,186 +404,198 @@ export default {
|
|||||||
#else
|
#else
|
||||||
#set($comment=$column.columnComment)
|
#set($comment=$column.columnComment)
|
||||||
#end
|
#end
|
||||||
$column.javaField: [
|
$column.javaField: [
|
||||||
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
|
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
|
||||||
]#if($foreach.count != $columns.size()),#end
|
]#if($foreach.count != $columns.size()),#end
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
},
|
|
||||||
created() {
|
const { queryParams, form, rules } = toRefs(data);
|
||||||
this.getList();
|
|
||||||
},
|
/** 查询${functionName}列表 */
|
||||||
methods: {
|
function getList() {
|
||||||
/** 查询${functionName}列表 */
|
loading.value = true;
|
||||||
getList() {
|
|
||||||
this.loading = true;
|
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
this.queryParams.params = {};
|
queryParams.value.params = {};
|
||||||
#break
|
#break
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||||
if (null != this.daterange${AttrName} && '' != this.daterange${AttrName}) {
|
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
|
||||||
this.queryParams.params["begin${AttrName}"] = this.daterange${AttrName}[0];
|
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0];
|
||||||
this.queryParams.params["end${AttrName}"] = this.daterange${AttrName}[1];
|
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1];
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
list${BusinessName}(this.queryParams).then(response => {
|
list${BusinessName}(queryParams.value).then(response => {
|
||||||
this.${businessName}List = response.rows;
|
${businessName}List.value = response.rows;
|
||||||
this.total = response.total;
|
total.value = response.total;
|
||||||
this.loading = false;
|
loading.value = false;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
// 取消按钮
|
|
||||||
cancel() {
|
// 取消按钮
|
||||||
this.open = false;
|
function cancel() {
|
||||||
this.reset();
|
open.value = false;
|
||||||
},
|
reset();
|
||||||
// 表单重置
|
}
|
||||||
reset() {
|
|
||||||
this.form = {
|
// 表单重置
|
||||||
|
function reset() {
|
||||||
|
form.value = {
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "checkbox")
|
#if($column.htmlType == "checkbox")
|
||||||
$column.javaField: []#if($foreach.count != $columns.size()),#end
|
$column.javaField: []#if($foreach.count != $columns.size()),#end
|
||||||
#else
|
#else
|
||||||
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
};
|
};
|
||||||
#if($table.sub)
|
#if($table.sub)
|
||||||
this.${subclassName}List = [];
|
${subclassName}List.value = [];
|
||||||
#end
|
#end
|
||||||
this.resetForm("form");
|
proxy.resetForm("${businessName}Ref");
|
||||||
},
|
}
|
||||||
/** 搜索按钮操作 */
|
|
||||||
handleQuery() {
|
/** 搜索按钮操作 */
|
||||||
this.queryParams.pageNum = 1;
|
function handleQuery() {
|
||||||
this.getList();
|
queryParams.value.pageNum = 1;
|
||||||
},
|
getList();
|
||||||
/** 重置按钮操作 */
|
}
|
||||||
resetQuery() {
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
function resetQuery() {
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
||||||
this.daterange${AttrName} = [];
|
daterange${AttrName}.value = [];
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
this.resetForm("queryForm");
|
proxy.resetForm("queryRef");
|
||||||
this.handleQuery();
|
handleQuery();
|
||||||
},
|
}
|
||||||
// 多选框选中数据
|
|
||||||
handleSelectionChange(selection) {
|
// 多选框选中数据
|
||||||
this.ids = selection.map(item => item.${pkColumn.javaField})
|
function handleSelectionChange(selection) {
|
||||||
this.single = selection.length!==1
|
ids.value = selection.map(item => item.${pkColumn.javaField});
|
||||||
this.multiple = !selection.length
|
single.value = selection.length != 1;
|
||||||
},
|
multiple.value = !selection.length;
|
||||||
/** 新增按钮操作 */
|
}
|
||||||
handleAdd() {
|
|
||||||
this.reset();
|
/** 新增按钮操作 */
|
||||||
this.open = true;
|
function handleAdd() {
|
||||||
this.title = "添加${functionName}";
|
reset();
|
||||||
},
|
open.value = true;
|
||||||
/** 修改按钮操作 */
|
title.value = "添加${functionName}";
|
||||||
handleUpdate(row) {
|
}
|
||||||
this.reset();
|
|
||||||
const ${pkColumn.javaField} = row.${pkColumn.javaField} || this.ids
|
/** 修改按钮操作 */
|
||||||
get${BusinessName}(${pkColumn.javaField}).then(response => {
|
function handleUpdate(row) {
|
||||||
this.form = response.data;
|
reset();
|
||||||
|
const _${pkColumn.javaField} = row.${pkColumn.javaField} || ids.value
|
||||||
|
get${BusinessName}(_${pkColumn.javaField}).then(response => {
|
||||||
|
form.value = response.data;
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "checkbox")
|
#if($column.htmlType == "checkbox")
|
||||||
this.form.$column.javaField = this.form.${column.javaField}.split(",");
|
form.value.$column.javaField = form.value.${column.javaField}.split(",");
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
#if($table.sub)
|
#if($table.sub)
|
||||||
this.${subclassName}List = response.data.${subclassName}List;
|
${subclassName}List.value = response.data.${subclassName}List;
|
||||||
#end
|
#end
|
||||||
this.open = true;
|
open.value = true;
|
||||||
this.title = "修改${functionName}";
|
title.value = "修改${functionName}";
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
/** 提交按钮 */
|
|
||||||
submitForm() {
|
/** 提交按钮 */
|
||||||
this.#[[$]]#refs["form"].validate(valid => {
|
function submitForm() {
|
||||||
if (valid) {
|
proxy.#[[$]]#refs["${businessName}Ref"].validate(valid => {
|
||||||
|
if (valid) {
|
||||||
#foreach ($column in $columns)
|
#foreach ($column in $columns)
|
||||||
#if($column.htmlType == "checkbox")
|
#if($column.htmlType == "checkbox")
|
||||||
this.form.$column.javaField = this.form.${column.javaField}.join(",");
|
form.value.$column.javaField = form.value.${column.javaField}.join(",");
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
#if($table.sub)
|
#if($table.sub)
|
||||||
this.form.${subclassName}List = this.${subclassName}List;
|
form.value.${subclassName}List = ${subclassName}List.value;
|
||||||
#end
|
#end
|
||||||
if (this.form.${pkColumn.javaField} != null) {
|
if (form.value.${pkColumn.javaField} != null) {
|
||||||
update${BusinessName}(this.form).then(response => {
|
update${BusinessName}(form.value).then(response => {
|
||||||
this.#[[$modal]]#.msgSuccess("修改成功");
|
proxy.#[[$modal]]#.msgSuccess("修改成功");
|
||||||
this.open = false;
|
open.value = false;
|
||||||
this.getList();
|
getList();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
add${BusinessName}(this.form).then(response => {
|
add${BusinessName}(form.value).then(response => {
|
||||||
this.#[[$modal]]#.msgSuccess("新增成功");
|
proxy.#[[$modal]]#.msgSuccess("新增成功");
|
||||||
this.open = false;
|
open.value = false;
|
||||||
this.getList();
|
getList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
/** 删除按钮操作 */
|
|
||||||
handleDelete(row) {
|
/** 删除按钮操作 */
|
||||||
const ${pkColumn.javaField}s = row.${pkColumn.javaField} || this.ids;
|
function handleDelete(row) {
|
||||||
this.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + ${pkColumn.javaField}s + '"的数据项?').then(function() {
|
const _${pkColumn.javaField}s = row.${pkColumn.javaField} || ids.value;
|
||||||
return del${BusinessName}(${pkColumn.javaField}s);
|
proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + _${pkColumn.javaField}s + '"的数据项?').then(function() {
|
||||||
}).then(() => {
|
return del${BusinessName}(_${pkColumn.javaField}s);
|
||||||
this.getList();
|
}).then(() => {
|
||||||
this.#[[$modal]]#.msgSuccess("删除成功");
|
getList();
|
||||||
}).catch(() => {});
|
proxy.#[[$modal]]#.msgSuccess("删除成功");
|
||||||
},
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
|
||||||
#if($table.sub)
|
#if($table.sub)
|
||||||
/** ${subTable.functionName}序号 */
|
/** ${subTable.functionName}序号 */
|
||||||
row${subClassName}Index({ row, rowIndex }) {
|
function row${subClassName}Index({ row, rowIndex }) {
|
||||||
row.index = rowIndex + 1;
|
row.index = rowIndex + 1;
|
||||||
},
|
}
|
||||||
/** ${subTable.functionName}添加按钮操作 */
|
|
||||||
handleAdd${subClassName}() {
|
/** ${subTable.functionName}添加按钮操作 */
|
||||||
let obj = {};
|
function handleAdd${subClassName}() {
|
||||||
|
let obj = {};
|
||||||
#foreach($column in $subTable.columns)
|
#foreach($column in $subTable.columns)
|
||||||
#if($column.pk || $column.javaField == ${subTableFkclassName})
|
#if($column.pk || $column.javaField == ${subTableFkclassName})
|
||||||
#elseif($column.list && "" != $javaField)
|
#elseif($column.list && "" != $javaField)
|
||||||
obj.$column.javaField = "";
|
obj.$column.javaField = "";
|
||||||
#end
|
#end
|
||||||
#end
|
#end
|
||||||
this.${subclassName}List.push(obj);
|
${subclassName}List.value.push(obj);
|
||||||
},
|
}
|
||||||
/** ${subTable.functionName}删除按钮操作 */
|
|
||||||
handleDelete${subClassName}() {
|
/** ${subTable.functionName}删除按钮操作 */
|
||||||
if (this.checked${subClassName}.length == 0) {
|
function handleDelete${subClassName}() {
|
||||||
this.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据");
|
if (checked${subClassName}.value.length == 0) {
|
||||||
} else {
|
proxy.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据");
|
||||||
const ${subclassName}List = this.${subclassName}List;
|
} else {
|
||||||
const checked${subClassName} = this.checked${subClassName};
|
const ${subclassName}s = ${subclassName}List.value;
|
||||||
this.${subclassName}List = ${subclassName}List.filter(function(item) {
|
const checked${subClassName}s = checked${subClassName}.value;
|
||||||
return checked${subClassName}.indexOf(item.index) == -1
|
${subclassName}List.value = ${subclassName}s.filter(function(item) {
|
||||||
});
|
return checked${subClassName}s.indexOf(item.index) == -1
|
||||||
}
|
});
|
||||||
},
|
|
||||||
/** 复选框选中数据 */
|
|
||||||
handle${subClassName}SelectionChange(selection) {
|
|
||||||
this.checked${subClassName} = selection.map(item => item.index)
|
|
||||||
},
|
|
||||||
#end
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
handleExport() {
|
|
||||||
this.download('${moduleName}/${businessName}/export', {
|
|
||||||
...this.queryParams
|
|
||||||
}, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/** 复选框选中数据 */
|
||||||
|
function handle${subClassName}SelectionChange(selection) {
|
||||||
|
checked${subClassName}.value = selection.map(item => item.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
#end
|
||||||
|
/** 导出按钮操作 */
|
||||||
|
function handleExport() {
|
||||||
|
proxy.download('${moduleName}/${businessName}/export', {
|
||||||
|
...queryParams.value
|
||||||
|
}, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)
|
||||||
|
}
|
||||||
|
|
||||||
|
getList();
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,474 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
|
||||||
#foreach($column in $columns)
|
|
||||||
#if($column.query)
|
|
||||||
#set($dictType=$column.dictType)
|
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
#if($column.htmlType == "input")
|
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.${column.javaField}"
|
|
||||||
placeholder="请输入${comment}"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
|
||||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in ${dictType}"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
|
||||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
|
|
||||||
<el-option label="请选择字典生成" value="" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
|
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
|
||||||
<el-date-picker clearable
|
|
||||||
v-model="queryParams.${column.javaField}"
|
|
||||||
type="date"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
placeholder="选择${comment}">
|
|
||||||
</el-date-picker>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
<el-form-item label="${comment}" style="width: 308px">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="daterange${AttrName}"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
type="daterange"
|
|
||||||
range-separator="-"
|
|
||||||
start-placeholder="开始日期"
|
|
||||||
end-placeholder="结束日期"
|
|
||||||
></el-date-picker>
|
|
||||||
</el-form-item>
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
||||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<el-row :gutter="10" class="mb8">
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
icon="Plus"
|
|
||||||
@click="handleAdd"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
|
||||||
>新增</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
type="info"
|
|
||||||
plain
|
|
||||||
icon="Sort"
|
|
||||||
@click="toggleExpandAll"
|
|
||||||
>展开/折叠</el-button>
|
|
||||||
</el-col>
|
|
||||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-table
|
|
||||||
v-if="refreshTable"
|
|
||||||
v-loading="loading"
|
|
||||||
:data="${businessName}List"
|
|
||||||
row-key="${treeCode}"
|
|
||||||
:default-expand-all="isExpandAll"
|
|
||||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
|
||||||
>
|
|
||||||
#foreach($column in $columns)
|
|
||||||
#set($javaField=$column.javaField)
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
#if($column.pk)
|
|
||||||
#elseif($column.list && $column.htmlType == "datetime")
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
|
|
||||||
<template #default="scope">
|
|
||||||
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && $column.htmlType == "imageUpload")
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && "" != $column.dictType)
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}">
|
|
||||||
<template #default="scope">
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
|
|
||||||
#else
|
|
||||||
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField}"/>
|
|
||||||
#end
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && "" != $javaField)
|
|
||||||
#if(${foreach.index} == 1)
|
|
||||||
<el-table-column label="${comment}" prop="${javaField}" />
|
|
||||||
#else
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" />
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
|
||||||
<el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['${moduleName}:${businessName}:add']">新增</el-button>
|
|
||||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<!-- 添加或修改${functionName}对话框 -->
|
|
||||||
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
|
||||||
<el-form ref="${businessName}Ref" :model="form" :rules="rules" label-width="80px">
|
|
||||||
#foreach($column in $columns)
|
|
||||||
#set($field=$column.javaField)
|
|
||||||
#if($column.insert && !$column.pk)
|
|
||||||
#if(($column.usableColumn) || (!$column.superColumn))
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
#set($dictType=$column.dictType)
|
|
||||||
#if("" != $treeParentCode && $column.javaField == $treeParentCode)
|
|
||||||
<el-form-item label="${comment}" prop="${treeParentCode}">
|
|
||||||
<el-tree-select
|
|
||||||
v-model="form.${treeParentCode}"
|
|
||||||
:data="${businessName}Options"
|
|
||||||
:props="{ value: '${treeCode}', label: '${treeName}', children: 'children' }"
|
|
||||||
value-key="${treeCode}"
|
|
||||||
placeholder="请选择${comment}"
|
|
||||||
check-strictly
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "input")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-input v-model="form.${field}" placeholder="请输入${comment}" />
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "imageUpload")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<image-upload v-model="form.${field}"/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "fileUpload")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<file-upload v-model="form.${field}"/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "editor")
|
|
||||||
<el-form-item label="${comment}">
|
|
||||||
<editor v-model="form.${field}" :min-height="192"/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "select" && "" != $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-select v-model="form.${field}" placeholder="请选择${comment}">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in ${dictType}"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
|
||||||
:value="parseInt(dict.value)"
|
|
||||||
#else
|
|
||||||
:value="dict.value"
|
|
||||||
#end
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "select" && $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-select v-model="form.${field}" placeholder="请选择${comment}">
|
|
||||||
<el-option label="请选择字典生成" value="" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "checkbox" && "" != $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-checkbox-group v-model="form.${field}">
|
|
||||||
<el-checkbox
|
|
||||||
v-for="dict in ${dictType}"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.value">
|
|
||||||
{{dict.label}}
|
|
||||||
</el-checkbox>
|
|
||||||
</el-checkbox-group>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "checkbox" && $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-checkbox-group v-model="form.${field}">
|
|
||||||
<el-checkbox>请选择字典生成</el-checkbox>
|
|
||||||
</el-checkbox-group>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "radio" && "" != $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-radio-group v-model="form.${field}">
|
|
||||||
<el-radio
|
|
||||||
v-for="dict in ${dictType}"
|
|
||||||
:key="dict.value"
|
|
||||||
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
|
||||||
:label="parseInt(dict.value)"
|
|
||||||
#else
|
|
||||||
:label="dict.value"
|
|
||||||
#end
|
|
||||||
>{{dict.label}}</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "radio" && $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-radio-group v-model="form.${field}">
|
|
||||||
<el-radio label="1">请选择字典生成</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "datetime")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-date-picker clearable
|
|
||||||
v-model="form.${field}"
|
|
||||||
type="date"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
placeholder="选择${comment}">
|
|
||||||
</el-date-picker>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "textarea")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
|
|
||||||
</el-form-item>
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
|
||||||
<el-button @click="cancel">取 消</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup name="${BusinessName}">
|
|
||||||
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
|
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
#if(${dicts} != '')
|
|
||||||
#set($dictsNoSymbol=$dicts.replace("'", ""))
|
|
||||||
const { ${dictsNoSymbol} } = proxy.useDict(${dicts});
|
|
||||||
#end
|
|
||||||
|
|
||||||
const ${businessName}List = ref([]);
|
|
||||||
const ${businessName}Options = ref([]);
|
|
||||||
const open = ref(false);
|
|
||||||
const loading = ref(true);
|
|
||||||
const showSearch = ref(true);
|
|
||||||
const title = ref("");
|
|
||||||
const isExpandAll = ref(true);
|
|
||||||
const refreshTable = ref(true);
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
|
||||||
const daterange${AttrName} = ref([]);
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
|
|
||||||
const data = reactive({
|
|
||||||
form: {},
|
|
||||||
queryParams: {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.query)
|
|
||||||
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.required)
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
$column.javaField: [
|
|
||||||
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
|
|
||||||
]#if($foreach.count != $columns.size()),#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const { queryParams, form, rules } = toRefs(data);
|
|
||||||
|
|
||||||
/** 查询${functionName}列表 */
|
|
||||||
function getList() {
|
|
||||||
loading.value = true;
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
queryParams.value.params = {};
|
|
||||||
#break
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
|
||||||
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
|
|
||||||
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0];
|
|
||||||
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1];
|
|
||||||
}
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
list${BusinessName}(queryParams.value).then(response => {
|
|
||||||
${businessName}List.value = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 查询${functionName}下拉树结构 */
|
|
||||||
function getTreeselect() {
|
|
||||||
list${BusinessName}().then(response => {
|
|
||||||
${businessName}Options.value = [];
|
|
||||||
const data = { ${treeCode}: 0, ${treeName}: '顶级节点', children: [] };
|
|
||||||
data.children = proxy.handleTree(response.data, "${treeCode}", "${treeParentCode}");
|
|
||||||
${businessName}Options.value.push(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消按钮
|
|
||||||
function cancel() {
|
|
||||||
open.value = false;
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表单重置
|
|
||||||
function reset() {
|
|
||||||
form.value = {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
$column.javaField: []#if($foreach.count != $columns.size()),#end
|
|
||||||
#else
|
|
||||||
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
};
|
|
||||||
proxy.resetForm("${businessName}Ref");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
function handleQuery() {
|
|
||||||
getList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置按钮操作 */
|
|
||||||
function resetQuery() {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
|
||||||
daterange${AttrName}.value = [];
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
proxy.resetForm("queryRef");
|
|
||||||
handleQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 新增按钮操作 */
|
|
||||||
function handleAdd(row) {
|
|
||||||
reset();
|
|
||||||
getTreeselect();
|
|
||||||
if (row != null && row.${treeCode}) {
|
|
||||||
form.value.${treeParentCode} = row.${treeCode};
|
|
||||||
} else {
|
|
||||||
form.value.${treeParentCode} = 0;
|
|
||||||
}
|
|
||||||
open.value = true;
|
|
||||||
title.value = "添加${functionName}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 展开/折叠操作 */
|
|
||||||
function toggleExpandAll() {
|
|
||||||
refreshTable.value = false;
|
|
||||||
isExpandAll.value = !isExpandAll.value;
|
|
||||||
nextTick(() => {
|
|
||||||
refreshTable.value = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 修改按钮操作 */
|
|
||||||
async function handleUpdate(row) {
|
|
||||||
reset();
|
|
||||||
await getTreeselect();
|
|
||||||
if (row != null) {
|
|
||||||
form.value.${treeParentCode} = row.${treeCode};
|
|
||||||
}
|
|
||||||
get${BusinessName}(row.${pkColumn.javaField}).then(response => {
|
|
||||||
form.value = response.data;
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
form.value.$column.javaField = form.value.${column.javaField}.split(",");
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
open.value = true;
|
|
||||||
title.value = "修改${functionName}";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交按钮 */
|
|
||||||
function submitForm() {
|
|
||||||
proxy.#[[$]]#refs["${businessName}Ref"].validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
form.value.$column.javaField = form.value.${column.javaField}.join(",");
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
if (form.value.${pkColumn.javaField} != null) {
|
|
||||||
update${BusinessName}(form.value).then(response => {
|
|
||||||
proxy.#[[$modal]]#.msgSuccess("修改成功");
|
|
||||||
open.value = false;
|
|
||||||
getList();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
add${BusinessName}(form.value).then(response => {
|
|
||||||
proxy.#[[$modal]]#.msgSuccess("新增成功");
|
|
||||||
open.value = false;
|
|
||||||
getList();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
function handleDelete(row) {
|
|
||||||
proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + row.${pkColumn.javaField} + '"的数据项?').then(function() {
|
|
||||||
return del${BusinessName}(row.${pkColumn.javaField});
|
|
||||||
}).then(() => {
|
|
||||||
getList();
|
|
||||||
proxy.#[[$modal]]#.msgSuccess("删除成功");
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
|
||||||
|
|
||||||
getList();
|
|
||||||
</script>
|
|
@ -1,590 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="app-container">
|
|
||||||
<el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px">
|
|
||||||
#foreach($column in $columns)
|
|
||||||
#if($column.query)
|
|
||||||
#set($dictType=$column.dictType)
|
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
#if($column.htmlType == "input")
|
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
|
||||||
<el-input
|
|
||||||
v-model="queryParams.${column.javaField}"
|
|
||||||
placeholder="请输入${comment}"
|
|
||||||
clearable
|
|
||||||
@keyup.enter="handleQuery"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
|
||||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
|
|
||||||
<el-option
|
|
||||||
v-for="dict in ${dictType}"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
|
||||||
<el-select v-model="queryParams.${column.javaField}" placeholder="请选择${comment}" clearable>
|
|
||||||
<el-option label="请选择字典生成" value="" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN")
|
|
||||||
<el-form-item label="${comment}" prop="${column.javaField}">
|
|
||||||
<el-date-picker clearable
|
|
||||||
v-model="queryParams.${column.javaField}"
|
|
||||||
type="date"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
placeholder="请选择${comment}">
|
|
||||||
</el-date-picker>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
<el-form-item label="${comment}" style="width: 308px">
|
|
||||||
<el-date-picker
|
|
||||||
v-model="daterange${AttrName}"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
type="daterange"
|
|
||||||
range-separator="-"
|
|
||||||
start-placeholder="开始日期"
|
|
||||||
end-placeholder="结束日期"
|
|
||||||
></el-date-picker>
|
|
||||||
</el-form-item>
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
<el-form-item>
|
|
||||||
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
|
||||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
|
|
||||||
<el-row :gutter="10" class="mb8">
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
plain
|
|
||||||
icon="Plus"
|
|
||||||
@click="handleAdd"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:add']"
|
|
||||||
>新增</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
type="success"
|
|
||||||
plain
|
|
||||||
icon="Edit"
|
|
||||||
:disabled="single"
|
|
||||||
@click="handleUpdate"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:edit']"
|
|
||||||
>修改</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
type="danger"
|
|
||||||
plain
|
|
||||||
icon="Delete"
|
|
||||||
:disabled="multiple"
|
|
||||||
@click="handleDelete"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:remove']"
|
|
||||||
>删除</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button
|
|
||||||
type="warning"
|
|
||||||
plain
|
|
||||||
icon="Download"
|
|
||||||
@click="handleExport"
|
|
||||||
v-hasPermi="['${moduleName}:${businessName}:export']"
|
|
||||||
>导出</el-button>
|
|
||||||
</el-col>
|
|
||||||
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
|
||||||
</el-row>
|
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="${businessName}List" @selection-change="handleSelectionChange">
|
|
||||||
<el-table-column type="selection" width="55" align="center" />
|
|
||||||
#foreach($column in $columns)
|
|
||||||
#set($javaField=$column.javaField)
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
#if($column.pk)
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" />
|
|
||||||
#elseif($column.list && $column.htmlType == "datetime")
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="180">
|
|
||||||
<template #default="scope">
|
|
||||||
<span>{{ parseTime(scope.row.${javaField}, '{y}-{m}-{d}') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && $column.htmlType == "imageUpload")
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" width="100">
|
|
||||||
<template #default="scope">
|
|
||||||
<image-preview :src="scope.row.${javaField}" :width="50" :height="50"/>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && "" != $column.dictType)
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}">
|
|
||||||
<template #default="scope">
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField} ? scope.row.${javaField}.split(',') : []"/>
|
|
||||||
#else
|
|
||||||
<dict-tag :options="${column.dictType}" :value="scope.row.${javaField}"/>
|
|
||||||
#end
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && "" != $javaField)
|
|
||||||
<el-table-column label="${comment}" align="center" prop="${javaField}" />
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['${moduleName}:${businessName}:edit']">修改</el-button>
|
|
||||||
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['${moduleName}:${businessName}:remove']">删除</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
|
|
||||||
<pagination
|
|
||||||
v-show="total>0"
|
|
||||||
:total="total"
|
|
||||||
v-model:page="queryParams.pageNum"
|
|
||||||
v-model:limit="queryParams.pageSize"
|
|
||||||
@pagination="getList"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- 添加或修改${functionName}对话框 -->
|
|
||||||
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
|
||||||
<el-form ref="${businessName}Ref" :model="form" :rules="rules" label-width="80px">
|
|
||||||
#foreach($column in $columns)
|
|
||||||
#set($field=$column.javaField)
|
|
||||||
#if($column.insert && !$column.pk)
|
|
||||||
#if(($column.usableColumn) || (!$column.superColumn))
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
#set($dictType=$column.dictType)
|
|
||||||
#if($column.htmlType == "input")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-input v-model="form.${field}" placeholder="请输入${comment}" />
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "imageUpload")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<image-upload v-model="form.${field}"/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "fileUpload")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<file-upload v-model="form.${field}"/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "editor")
|
|
||||||
<el-form-item label="${comment}">
|
|
||||||
<editor v-model="form.${field}" :min-height="192"/>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "select" && "" != $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-select v-model="form.${field}" placeholder="请选择${comment}">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in ${dictType}"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
|
||||||
:value="parseInt(dict.value)"
|
|
||||||
#else
|
|
||||||
:value="dict.value"
|
|
||||||
#end
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "select" && $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-select v-model="form.${field}" placeholder="请选择${comment}">
|
|
||||||
<el-option label="请选择字典生成" value="" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "checkbox" && "" != $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-checkbox-group v-model="form.${field}">
|
|
||||||
<el-checkbox
|
|
||||||
v-for="dict in ${dictType}"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.value">
|
|
||||||
{{dict.label}}
|
|
||||||
</el-checkbox>
|
|
||||||
</el-checkbox-group>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "checkbox" && $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-checkbox-group v-model="form.${field}">
|
|
||||||
<el-checkbox>请选择字典生成</el-checkbox>
|
|
||||||
</el-checkbox-group>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "radio" && "" != $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-radio-group v-model="form.${field}">
|
|
||||||
<el-radio
|
|
||||||
v-for="dict in ${dictType}"
|
|
||||||
:key="dict.value"
|
|
||||||
#if($column.javaType == "Integer" || $column.javaType == "Long")
|
|
||||||
:label="parseInt(dict.value)"
|
|
||||||
#else
|
|
||||||
:label="dict.value"
|
|
||||||
#end
|
|
||||||
>{{dict.label}}</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "radio" && $dictType)
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-radio-group v-model="form.${field}">
|
|
||||||
<el-radio label="1">请选择字典生成</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "datetime")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-date-picker clearable
|
|
||||||
v-model="form.${field}"
|
|
||||||
type="date"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
placeholder="请选择${comment}">
|
|
||||||
</el-date-picker>
|
|
||||||
</el-form-item>
|
|
||||||
#elseif($column.htmlType == "textarea")
|
|
||||||
<el-form-item label="${comment}" prop="${field}">
|
|
||||||
<el-input v-model="form.${field}" type="textarea" placeholder="请输入内容" />
|
|
||||||
</el-form-item>
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#if($table.sub)
|
|
||||||
<el-divider content-position="center">${subTable.functionName}信息</el-divider>
|
|
||||||
<el-row :gutter="10" class="mb8">
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button type="primary" icon="Plus" @click="handleAdd${subClassName}">添加</el-button>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="1.5">
|
|
||||||
<el-button type="danger" icon="Delete" @click="handleDelete${subClassName}">删除</el-button>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
<el-table :data="${subclassName}List" :row-class-name="row${subClassName}Index" @selection-change="handle${subClassName}SelectionChange" ref="${subclassName}">
|
|
||||||
<el-table-column type="selection" width="50" align="center" />
|
|
||||||
<el-table-column label="序号" align="center" prop="index" width="50"/>
|
|
||||||
#foreach($column in $subTable.columns)
|
|
||||||
#set($javaField=$column.javaField)
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
#if($column.pk || $javaField == ${subTableFkclassName})
|
|
||||||
#elseif($column.list && $column.htmlType == "input")
|
|
||||||
<el-table-column label="$comment" prop="${javaField}" width="150">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-input v-model="scope.row.$javaField" placeholder="请输入$comment" />
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && $column.htmlType == "datetime")
|
|
||||||
<el-table-column label="$comment" prop="${javaField}" width="240">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-date-picker clearable
|
|
||||||
v-model="scope.row.$javaField"
|
|
||||||
type="date"
|
|
||||||
value-format="YYYY-MM-DD"
|
|
||||||
placeholder="请选择$comment">
|
|
||||||
</el-date-picker>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" != $column.dictType)
|
|
||||||
<el-table-column label="$comment" prop="${javaField}" width="150">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
|
|
||||||
<el-option
|
|
||||||
v-for="dict in $column.dictType"
|
|
||||||
:key="dict.value"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
></el-option>
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#elseif($column.list && ($column.htmlType == "select" || $column.htmlType == "radio") && "" == $column.dictType)
|
|
||||||
<el-table-column label="$comment" prop="${javaField}" width="150">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-select v-model="scope.row.$javaField" placeholder="请选择$comment">
|
|
||||||
<el-option label="请选择字典生成" value="" />
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
</el-table>
|
|
||||||
#end
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<div class="dialog-footer">
|
|
||||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
|
||||||
<el-button @click="cancel">取 消</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup name="${BusinessName}">
|
|
||||||
import { list${BusinessName}, get${BusinessName}, del${BusinessName}, add${BusinessName}, update${BusinessName} } from "@/api/${moduleName}/${businessName}";
|
|
||||||
|
|
||||||
const { proxy } = getCurrentInstance();
|
|
||||||
#if(${dicts} != '')
|
|
||||||
#set($dictsNoSymbol=$dicts.replace("'", ""))
|
|
||||||
const { ${dictsNoSymbol} } = proxy.useDict(${dicts});
|
|
||||||
#end
|
|
||||||
|
|
||||||
const ${businessName}List = ref([]);
|
|
||||||
#if($table.sub)
|
|
||||||
const ${subclassName}List = ref([]);
|
|
||||||
#end
|
|
||||||
const open = ref(false);
|
|
||||||
const loading = ref(true);
|
|
||||||
const showSearch = ref(true);
|
|
||||||
const ids = ref([]);
|
|
||||||
#if($table.sub)
|
|
||||||
const checked${subClassName} = ref([]);
|
|
||||||
#end
|
|
||||||
const single = ref(true);
|
|
||||||
const multiple = ref(true);
|
|
||||||
const total = ref(0);
|
|
||||||
const title = ref("");
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
|
||||||
const daterange${AttrName} = ref([]);
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
|
|
||||||
const data = reactive({
|
|
||||||
form: {},
|
|
||||||
queryParams: {
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.query)
|
|
||||||
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.required)
|
|
||||||
#set($parentheseIndex=$column.columnComment.indexOf("("))
|
|
||||||
#if($parentheseIndex != -1)
|
|
||||||
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
|
|
||||||
#else
|
|
||||||
#set($comment=$column.columnComment)
|
|
||||||
#end
|
|
||||||
$column.javaField: [
|
|
||||||
{ required: true, message: "$comment不能为空", trigger: #if($column.htmlType == "select" || $column.htmlType == "radio")"change"#else"blur"#end }
|
|
||||||
]#if($foreach.count != $columns.size()),#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const { queryParams, form, rules } = toRefs(data);
|
|
||||||
|
|
||||||
/** 查询${functionName}列表 */
|
|
||||||
function getList() {
|
|
||||||
loading.value = true;
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
queryParams.value.params = {};
|
|
||||||
#break
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
|
||||||
if (null != daterange${AttrName} && '' != daterange${AttrName}) {
|
|
||||||
queryParams.value.params["begin${AttrName}"] = daterange${AttrName}.value[0];
|
|
||||||
queryParams.value.params["end${AttrName}"] = daterange${AttrName}.value[1];
|
|
||||||
}
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
list${BusinessName}(queryParams.value).then(response => {
|
|
||||||
${businessName}List.value = response.rows;
|
|
||||||
total.value = response.total;
|
|
||||||
loading.value = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 取消按钮
|
|
||||||
function cancel() {
|
|
||||||
open.value = false;
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表单重置
|
|
||||||
function reset() {
|
|
||||||
form.value = {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
$column.javaField: []#if($foreach.count != $columns.size()),#end
|
|
||||||
#else
|
|
||||||
$column.javaField: null#if($foreach.count != $columns.size()),#end
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
};
|
|
||||||
#if($table.sub)
|
|
||||||
${subclassName}List.value = [];
|
|
||||||
#end
|
|
||||||
proxy.resetForm("${businessName}Ref");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 搜索按钮操作 */
|
|
||||||
function handleQuery() {
|
|
||||||
queryParams.value.pageNum = 1;
|
|
||||||
getList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 重置按钮操作 */
|
|
||||||
function resetQuery() {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "datetime" && $column.queryType == "BETWEEN")
|
|
||||||
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
|
|
||||||
daterange${AttrName}.value = [];
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
proxy.resetForm("queryRef");
|
|
||||||
handleQuery();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 多选框选中数据
|
|
||||||
function handleSelectionChange(selection) {
|
|
||||||
ids.value = selection.map(item => item.${pkColumn.javaField});
|
|
||||||
single.value = selection.length != 1;
|
|
||||||
multiple.value = !selection.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 新增按钮操作 */
|
|
||||||
function handleAdd() {
|
|
||||||
reset();
|
|
||||||
open.value = true;
|
|
||||||
title.value = "添加${functionName}";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 修改按钮操作 */
|
|
||||||
function handleUpdate(row) {
|
|
||||||
reset();
|
|
||||||
const _${pkColumn.javaField} = row.${pkColumn.javaField} || ids.value
|
|
||||||
get${BusinessName}(_${pkColumn.javaField}).then(response => {
|
|
||||||
form.value = response.data;
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
form.value.$column.javaField = form.value.${column.javaField}.split(",");
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#if($table.sub)
|
|
||||||
${subclassName}List.value = response.data.${subclassName}List;
|
|
||||||
#end
|
|
||||||
open.value = true;
|
|
||||||
title.value = "修改${functionName}";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 提交按钮 */
|
|
||||||
function submitForm() {
|
|
||||||
proxy.#[[$]]#refs["${businessName}Ref"].validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
#foreach ($column in $columns)
|
|
||||||
#if($column.htmlType == "checkbox")
|
|
||||||
form.value.$column.javaField = form.value.${column.javaField}.join(",");
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
#if($table.sub)
|
|
||||||
form.value.${subclassName}List = ${subclassName}List.value;
|
|
||||||
#end
|
|
||||||
if (form.value.${pkColumn.javaField} != null) {
|
|
||||||
update${BusinessName}(form.value).then(response => {
|
|
||||||
proxy.#[[$modal]]#.msgSuccess("修改成功");
|
|
||||||
open.value = false;
|
|
||||||
getList();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
add${BusinessName}(form.value).then(response => {
|
|
||||||
proxy.#[[$modal]]#.msgSuccess("新增成功");
|
|
||||||
open.value = false;
|
|
||||||
getList();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
|
||||||
function handleDelete(row) {
|
|
||||||
const _${pkColumn.javaField}s = row.${pkColumn.javaField} || ids.value;
|
|
||||||
proxy.#[[$modal]]#.confirm('是否确认删除${functionName}编号为"' + _${pkColumn.javaField}s + '"的数据项?').then(function() {
|
|
||||||
return del${BusinessName}(_${pkColumn.javaField}s);
|
|
||||||
}).then(() => {
|
|
||||||
getList();
|
|
||||||
proxy.#[[$modal]]#.msgSuccess("删除成功");
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
|
||||||
|
|
||||||
#if($table.sub)
|
|
||||||
/** ${subTable.functionName}序号 */
|
|
||||||
function row${subClassName}Index({ row, rowIndex }) {
|
|
||||||
row.index = rowIndex + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** ${subTable.functionName}添加按钮操作 */
|
|
||||||
function handleAdd${subClassName}() {
|
|
||||||
let obj = {};
|
|
||||||
#foreach($column in $subTable.columns)
|
|
||||||
#if($column.pk || $column.javaField == ${subTableFkclassName})
|
|
||||||
#elseif($column.list && "" != $javaField)
|
|
||||||
obj.$column.javaField = "";
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
${subclassName}List.value.push(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** ${subTable.functionName}删除按钮操作 */
|
|
||||||
function handleDelete${subClassName}() {
|
|
||||||
if (checked${subClassName}.value.length == 0) {
|
|
||||||
proxy.#[[$modal]]#.msgError("请先选择要删除的${subTable.functionName}数据");
|
|
||||||
} else {
|
|
||||||
const ${subclassName}s = ${subclassName}List.value;
|
|
||||||
const checked${subClassName}s = checked${subClassName}.value;
|
|
||||||
${subclassName}List.value = ${subclassName}s.filter(function(item) {
|
|
||||||
return checked${subClassName}s.indexOf(item.index) == -1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 复选框选中数据 */
|
|
||||||
function handle${subClassName}SelectionChange(selection) {
|
|
||||||
checked${subClassName}.value = selection.map(item => item.index)
|
|
||||||
}
|
|
||||||
|
|
||||||
#end
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
function handleExport() {
|
|
||||||
proxy.download('${moduleName}/${businessName}/export', {
|
|
||||||
...queryParams.value
|
|
||||||
}, `${businessName}_#[[${new Date().getTime()}]]#.xlsx`)
|
|
||||||
}
|
|
||||||
|
|
||||||
getList();
|
|
||||||
</script>
|
|
@ -68,6 +68,7 @@ public class SysLogininforController extends BaseController
|
|||||||
*/
|
*/
|
||||||
@SaCheckPermission("monitor:logininfor:remove")
|
@SaCheckPermission("monitor:logininfor:remove")
|
||||||
@Log(title = "登录日志", businessType = BusinessType.DELETE)
|
@Log(title = "登录日志", businessType = BusinessType.DELETE)
|
||||||
|
@DeleteMapping("/{infoIds}")
|
||||||
public AjaxResult remove(@PathVariable Long[] infoIds)
|
public AjaxResult remove(@PathVariable Long[] infoIds)
|
||||||
{
|
{
|
||||||
return toAjax(logininforService.deleteLogininforByIds(infoIds));
|
return toAjax(logininforService.deleteLogininforByIds(infoIds));
|
||||||
|
@ -48,7 +48,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
|
|||||||
@DataScope(deptAlias = "d")
|
@DataScope(deptAlias = "d")
|
||||||
public List<SysDept> selectDeptList(SysDept dept) {
|
public List<SysDept> selectDeptList(SysDept dept) {
|
||||||
// 只查询未禁用部门
|
// 只查询未禁用部门
|
||||||
dept.setStatus(UserConstants.DEPT_NORMAL);
|
//dept.setStatus(UserConstants.DEPT_NORMAL);
|
||||||
return deptMapper.selectDeptList(dept);
|
return deptMapper.selectDeptList(dept);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = Ruoyi-Flex管理系统
|
VITE_APP_TITLE = Ruoyi-Flex管理系统
|
||||||
|
|
||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
ENV = 'development'
|
VITE_APP_ENV = 'development'
|
||||||
|
|
||||||
# 若依管理系统/开发环境
|
# 若依管理系统/开发环境
|
||||||
VUE_APP_BASE_API = '/dev-api'
|
VITE_APP_BASE_API = '/dev-api'
|
||||||
|
|
||||||
# 监控地址
|
# 监控地址
|
||||||
VUE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications'
|
VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications'
|
||||||
|
|
||||||
# powerjob任务调度控制台地址
|
# powerjob任务调度控制台地址
|
||||||
VUE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
|
VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
|
||||||
|
|
||||||
# 路由懒加载
|
# 路由懒加载
|
||||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
VITE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = Ruoyi-Flex管理系统
|
VITE_APP_TITLE = Ruoyi-Flex管理系统
|
||||||
|
|
||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
ENV = 'production'
|
VITE_APP_ENV = 'production'
|
||||||
|
|
||||||
# 若依管理系统/生产环境
|
# 若依管理系统/生产环境
|
||||||
VUE_APP_BASE_API = '/prod-api'
|
VITE_APP_BASE_API = '/prod-api'
|
||||||
|
|
||||||
# 监控地址
|
# 监控地址
|
||||||
VUE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications'
|
VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications'
|
||||||
|
|
||||||
# powerjob任务调度控制台地址
|
# powerjob任务调度控制台地址
|
||||||
VUE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
|
VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
|
||||||
|
|
||||||
|
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||||
|
VITE_BUILD_COMPRESS = gzip
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
# 页面标题
|
# 页面标题
|
||||||
VUE_APP_TITLE = Ruoyi-Flex管理系统
|
VITE_APP_TITLE = Ruoyi-Flex管理系统
|
||||||
|
|
||||||
NODE_ENV = production
|
# 生产环境配置
|
||||||
|
VITE_APP_ENV = 'staging'
|
||||||
|
|
||||||
# 测试环境配置
|
# 若依管理系统/生产环境
|
||||||
ENV = 'staging'
|
VITE_APP_BASE_API = '/stage-api'
|
||||||
|
|
||||||
# 若依管理系统/测试环境
|
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||||
VUE_APP_BASE_API = '/stage-api'
|
VITE_BUILD_COMPRESS = gzip
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
# 忽略build目录下类型为js的文件的语法检查
|
|
||||||
build/*.js
|
|
||||||
# 忽略src/assets目录下文件的语法检查
|
|
||||||
src/assets
|
|
||||||
# 忽略public目录下文件的语法检查
|
|
||||||
public
|
|
||||||
# 忽略当前目录下为js的文件的语法检查
|
|
||||||
*.js
|
|
||||||
# 忽略当前目录下为vue的文件的语法检查
|
|
||||||
*.vue
|
|
@ -1,199 +0,0 @@
|
|||||||
// ESlint 检查配置
|
|
||||||
module.exports = {
|
|
||||||
root: true,
|
|
||||||
parserOptions: {
|
|
||||||
parser: 'babel-eslint',
|
|
||||||
sourceType: 'module'
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
browser: true,
|
|
||||||
node: true,
|
|
||||||
es6: true,
|
|
||||||
},
|
|
||||||
extends: ['plugin:vue/recommended', 'eslint:recommended'],
|
|
||||||
|
|
||||||
// add your custom rules here
|
|
||||||
//it is base on https://github.com/vuejs/eslint-config-vue
|
|
||||||
rules: {
|
|
||||||
"vue/max-attributes-per-line": [2, {
|
|
||||||
"singleline": 10,
|
|
||||||
"multiline": {
|
|
||||||
"max": 1,
|
|
||||||
"allowFirstLine": false
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
"vue/singleline-html-element-content-newline": "off",
|
|
||||||
"vue/multiline-html-element-content-newline":"off",
|
|
||||||
"vue/name-property-casing": ["error", "PascalCase"],
|
|
||||||
"vue/no-v-html": "off",
|
|
||||||
'accessor-pairs': 2,
|
|
||||||
'arrow-spacing': [2, {
|
|
||||||
'before': true,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'block-spacing': [2, 'always'],
|
|
||||||
'brace-style': [2, '1tbs', {
|
|
||||||
'allowSingleLine': true
|
|
||||||
}],
|
|
||||||
'camelcase': [0, {
|
|
||||||
'properties': 'always'
|
|
||||||
}],
|
|
||||||
'comma-dangle': [2, 'never'],
|
|
||||||
'comma-spacing': [2, {
|
|
||||||
'before': false,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'comma-style': [2, 'last'],
|
|
||||||
'constructor-super': 2,
|
|
||||||
'curly': [2, 'multi-line'],
|
|
||||||
'dot-location': [2, 'property'],
|
|
||||||
'eol-last': 2,
|
|
||||||
'eqeqeq': ["error", "always", {"null": "ignore"}],
|
|
||||||
'generator-star-spacing': [2, {
|
|
||||||
'before': true,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'handle-callback-err': [2, '^(err|error)$'],
|
|
||||||
'indent': [2, 2, {
|
|
||||||
'SwitchCase': 1
|
|
||||||
}],
|
|
||||||
'jsx-quotes': [2, 'prefer-single'],
|
|
||||||
'key-spacing': [2, {
|
|
||||||
'beforeColon': false,
|
|
||||||
'afterColon': true
|
|
||||||
}],
|
|
||||||
'keyword-spacing': [2, {
|
|
||||||
'before': true,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'new-cap': [2, {
|
|
||||||
'newIsCap': true,
|
|
||||||
'capIsNew': false
|
|
||||||
}],
|
|
||||||
'new-parens': 2,
|
|
||||||
'no-array-constructor': 2,
|
|
||||||
'no-caller': 2,
|
|
||||||
'no-console': 'off',
|
|
||||||
'no-class-assign': 2,
|
|
||||||
'no-cond-assign': 2,
|
|
||||||
'no-const-assign': 2,
|
|
||||||
'no-control-regex': 0,
|
|
||||||
'no-delete-var': 2,
|
|
||||||
'no-dupe-args': 2,
|
|
||||||
'no-dupe-class-members': 2,
|
|
||||||
'no-dupe-keys': 2,
|
|
||||||
'no-duplicate-case': 2,
|
|
||||||
'no-empty-character-class': 2,
|
|
||||||
'no-empty-pattern': 2,
|
|
||||||
'no-eval': 2,
|
|
||||||
'no-ex-assign': 2,
|
|
||||||
'no-extend-native': 2,
|
|
||||||
'no-extra-bind': 2,
|
|
||||||
'no-extra-boolean-cast': 2,
|
|
||||||
'no-extra-parens': [2, 'functions'],
|
|
||||||
'no-fallthrough': 2,
|
|
||||||
'no-floating-decimal': 2,
|
|
||||||
'no-func-assign': 2,
|
|
||||||
'no-implied-eval': 2,
|
|
||||||
'no-inner-declarations': [2, 'functions'],
|
|
||||||
'no-invalid-regexp': 2,
|
|
||||||
'no-irregular-whitespace': 2,
|
|
||||||
'no-iterator': 2,
|
|
||||||
'no-label-var': 2,
|
|
||||||
'no-labels': [2, {
|
|
||||||
'allowLoop': false,
|
|
||||||
'allowSwitch': false
|
|
||||||
}],
|
|
||||||
'no-lone-blocks': 2,
|
|
||||||
'no-mixed-spaces-and-tabs': 2,
|
|
||||||
'no-multi-spaces': 2,
|
|
||||||
'no-multi-str': 2,
|
|
||||||
'no-multiple-empty-lines': [2, {
|
|
||||||
'max': 1
|
|
||||||
}],
|
|
||||||
'no-native-reassign': 2,
|
|
||||||
'no-negated-in-lhs': 2,
|
|
||||||
'no-new-object': 2,
|
|
||||||
'no-new-require': 2,
|
|
||||||
'no-new-symbol': 2,
|
|
||||||
'no-new-wrappers': 2,
|
|
||||||
'no-obj-calls': 2,
|
|
||||||
'no-octal': 2,
|
|
||||||
'no-octal-escape': 2,
|
|
||||||
'no-path-concat': 2,
|
|
||||||
'no-proto': 2,
|
|
||||||
'no-redeclare': 2,
|
|
||||||
'no-regex-spaces': 2,
|
|
||||||
'no-return-assign': [2, 'except-parens'],
|
|
||||||
'no-self-assign': 2,
|
|
||||||
'no-self-compare': 2,
|
|
||||||
'no-sequences': 2,
|
|
||||||
'no-shadow-restricted-names': 2,
|
|
||||||
'no-spaced-func': 2,
|
|
||||||
'no-sparse-arrays': 2,
|
|
||||||
'no-this-before-super': 2,
|
|
||||||
'no-throw-literal': 2,
|
|
||||||
'no-trailing-spaces': 2,
|
|
||||||
'no-undef': 2,
|
|
||||||
'no-undef-init': 2,
|
|
||||||
'no-unexpected-multiline': 2,
|
|
||||||
'no-unmodified-loop-condition': 2,
|
|
||||||
'no-unneeded-ternary': [2, {
|
|
||||||
'defaultAssignment': false
|
|
||||||
}],
|
|
||||||
'no-unreachable': 2,
|
|
||||||
'no-unsafe-finally': 2,
|
|
||||||
'no-unused-vars': [2, {
|
|
||||||
'vars': 'all',
|
|
||||||
'args': 'none'
|
|
||||||
}],
|
|
||||||
'no-useless-call': 2,
|
|
||||||
'no-useless-computed-key': 2,
|
|
||||||
'no-useless-constructor': 2,
|
|
||||||
'no-useless-escape': 0,
|
|
||||||
'no-whitespace-before-property': 2,
|
|
||||||
'no-with': 2,
|
|
||||||
'one-var': [2, {
|
|
||||||
'initialized': 'never'
|
|
||||||
}],
|
|
||||||
'operator-linebreak': [2, 'after', {
|
|
||||||
'overrides': {
|
|
||||||
'?': 'before',
|
|
||||||
':': 'before'
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
'padded-blocks': [2, 'never'],
|
|
||||||
'quotes': [2, 'single', {
|
|
||||||
'avoidEscape': true,
|
|
||||||
'allowTemplateLiterals': true
|
|
||||||
}],
|
|
||||||
'semi': [2, 'never'],
|
|
||||||
'semi-spacing': [2, {
|
|
||||||
'before': false,
|
|
||||||
'after': true
|
|
||||||
}],
|
|
||||||
'space-before-blocks': [2, 'always'],
|
|
||||||
'space-before-function-paren': [2, 'never'],
|
|
||||||
'space-in-parens': [2, 'never'],
|
|
||||||
'space-infix-ops': 2,
|
|
||||||
'space-unary-ops': [2, {
|
|
||||||
'words': true,
|
|
||||||
'nonwords': false
|
|
||||||
}],
|
|
||||||
'spaced-comment': [2, 'always', {
|
|
||||||
'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ',']
|
|
||||||
}],
|
|
||||||
'template-curly-spacing': [2, 'never'],
|
|
||||||
'use-isnan': 2,
|
|
||||||
'valid-typeof': 2,
|
|
||||||
'wrap-iife': [2, 'any'],
|
|
||||||
'yield-star-spacing': [2, 'both'],
|
|
||||||
'yoda': [2, 'never'],
|
|
||||||
'prefer-const': 2,
|
|
||||||
'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
|
|
||||||
'object-curly-spacing': [2, 'always', {
|
|
||||||
objectsInObjects: false
|
|
||||||
}],
|
|
||||||
'array-bracket-spacing': [2, 'never']
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,22 +2,18 @@
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 克隆项目
|
# 克隆项目
|
||||||
git clone https://gitee.com/y_project/RuoYi-Vue
|
git clone https://gitee.com/dataprince/ruoyi-flex
|
||||||
|
|
||||||
# 进入项目目录
|
# 进入项目目录
|
||||||
cd ruoyi-ui
|
cd ruoyi-ui
|
||||||
|
|
||||||
# 安装依赖
|
# 安装依赖
|
||||||
npm install
|
|
||||||
|
|
||||||
# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题
|
|
||||||
npm install --registry=https://registry.npmmirror.com
|
npm install --registry=https://registry.npmmirror.com
|
||||||
|
|
||||||
# 启动服务
|
# 启动服务
|
||||||
npm run dev
|
npm run dev
|
||||||
```
|
|
||||||
|
|
||||||
浏览器访问 http://localhost:80
|
前端浏览器访问 http://localhost:80
|
||||||
|
|
||||||
## 发布
|
## 发布
|
||||||
|
|
||||||
|
@ -7,6 +7,6 @@ echo.
|
|||||||
cd %~dp0
|
cd %~dp0
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
npm run build:prod
|
yarn build:prod
|
||||||
|
|
||||||
pause
|
pause
|
@ -7,6 +7,6 @@ echo.
|
|||||||
cd %~dp0
|
cd %~dp0
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
npm install --registry=https://registry.npmmirror.com
|
yarn --registry=https://registry.npm.taobao.org
|
||||||
|
|
||||||
pause
|
pause
|
@ -1,12 +1,12 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo.
|
echo.
|
||||||
echo [信息] 使用 Vue CLI 命令运行 Web 工程。
|
echo [信息] 使用 Vite 命令运行 Web 工程。
|
||||||
echo.
|
echo.
|
||||||
|
|
||||||
%~d0
|
%~d0
|
||||||
cd %~dp0
|
cd %~dp0
|
||||||
|
|
||||||
cd ..
|
cd ..
|
||||||
npm run dev
|
yarn dev
|
||||||
|
|
||||||
pause
|
pause
|
@ -1,35 +0,0 @@
|
|||||||
const { run } = require('runjs')
|
|
||||||
const chalk = require('chalk')
|
|
||||||
const config = require('../vue.config.js')
|
|
||||||
const rawArgv = process.argv.slice(2)
|
|
||||||
const args = rawArgv.join(' ')
|
|
||||||
|
|
||||||
if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
|
|
||||||
const report = rawArgv.includes('--report')
|
|
||||||
|
|
||||||
run(`vue-cli-service build ${args}`)
|
|
||||||
|
|
||||||
const port = 9526
|
|
||||||
const publicPath = config.publicPath
|
|
||||||
|
|
||||||
var connect = require('connect')
|
|
||||||
var serveStatic = require('serve-static')
|
|
||||||
const app = connect()
|
|
||||||
|
|
||||||
app.use(
|
|
||||||
publicPath,
|
|
||||||
serveStatic('./dist', {
|
|
||||||
index: ['index.html', '/']
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
app.listen(port, function () {
|
|
||||||
console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`))
|
|
||||||
if (report) {
|
|
||||||
console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`))
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
run(`vue-cli-service build ${args}`)
|
|
||||||
}
|
|
@ -1,14 +1,15 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
<head>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
<meta charset="utf-8">
|
||||||
<meta name="renderer" content="webkit">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<meta name="renderer" content="webkit">
|
||||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
<title><%= webpackConfig.name %></title>
|
<link rel="icon" href="/favicon.ico">
|
||||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
<title>Ruoyi-Flex管理系统</title>
|
||||||
<style>
|
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||||
|
<style>
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
#app {
|
#app {
|
||||||
@ -16,6 +17,7 @@
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.chromeframe {
|
.chromeframe {
|
||||||
margin: 0.2em 0;
|
margin: 0.2em 0;
|
||||||
background: #ccc;
|
background: #ccc;
|
||||||
@ -92,6 +94,7 @@
|
|||||||
-ms-transform: rotate(0deg);
|
-ms-transform: rotate(0deg);
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: rotate(360deg);
|
-webkit-transform: rotate(360deg);
|
||||||
-ms-transform: rotate(360deg);
|
-ms-transform: rotate(360deg);
|
||||||
@ -105,6 +108,7 @@
|
|||||||
-ms-transform: rotate(0deg);
|
-ms-transform: rotate(0deg);
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
-webkit-transform: rotate(360deg);
|
-webkit-transform: rotate(360deg);
|
||||||
-ms-transform: rotate(360deg);
|
-ms-transform: rotate(360deg);
|
||||||
@ -194,15 +198,18 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
|
||||||
<div id="app">
|
<body>
|
||||||
<div id="loader-wrapper">
|
<div id="app">
|
||||||
<div id="loader"></div>
|
<div id="loader-wrapper">
|
||||||
<div class="loader-section section-left"></div>
|
<div id="loader"></div>
|
||||||
<div class="loader-section section-right"></div>
|
<div class="loader-section section-left"></div>
|
||||||
<div class="load_title">正在加载系统资源,请耐心等待</div>
|
<div class="loader-section section-right"></div>
|
||||||
</div>
|
<div class="load_title">正在加载系统资源,请耐心等待</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</div>
|
||||||
|
<script type="module" src="/src/main.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
@ -5,11 +5,10 @@
|
|||||||
"author": "数据小王子",
|
"author": "数据小王子",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vue-cli-service serve",
|
"dev": "vite serve --mode development",
|
||||||
"build:prod": "vue-cli-service build",
|
"build:prod": "vite build --mode production &&vue-tsc --noEmit",
|
||||||
"build:stage": "vue-cli-service build --mode staging",
|
"build:stage": "vite build --mode staging",
|
||||||
"preview": "node build/index.js --preview",
|
"preview": "vite preview"
|
||||||
"lint": "eslint --ext .js,.vue src"
|
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
@ -36,48 +35,49 @@
|
|||||||
"url": "https://gitee.com/dataprince/ruoyi-flex.git"
|
"url": "https://gitee.com/dataprince/ruoyi-flex.git"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@riophae/vue-treeselect": "0.4.0",
|
"@element-plus/icons-vue": "2.0.10",
|
||||||
"axios": "0.24.0",
|
"@vueup/vue-quill": "1.1.0",
|
||||||
"clipboard": "2.0.8",
|
"@vueuse/core": "9.5.0",
|
||||||
"core-js": "3.25.3",
|
"@zeronejs/utils": "^1.4.0",
|
||||||
|
"axios": "0.27.2",
|
||||||
"echarts": "5.4.0",
|
"echarts": "5.4.0",
|
||||||
"element-ui": "2.15.13",
|
"element-plus": "2.2.27",
|
||||||
"file-saver": "2.0.5",
|
"file-saver": "2.0.5",
|
||||||
"fuse.js": "6.4.3",
|
"fuse.js": "6.6.2",
|
||||||
"highlight.js": "9.18.5",
|
|
||||||
"js-beautify": "1.13.0",
|
|
||||||
"js-cookie": "3.0.1",
|
"js-cookie": "3.0.1",
|
||||||
"jsencrypt": "3.0.0-rc.1",
|
"jsencrypt": "3.3.1",
|
||||||
"nprogress": "0.2.0",
|
"nprogress": "0.2.0",
|
||||||
"quill": "1.3.7",
|
"vue": "3.2.45",
|
||||||
"screenfull": "5.0.2",
|
"vue-cropper": "1.0.3",
|
||||||
"sortablejs": "1.10.2",
|
"vue-router": "4.1.4",
|
||||||
"vue": "2.6.12",
|
"vuex": "4.0.2"
|
||||||
"vue-count-to": "1.0.13",
|
|
||||||
"vue-cropper": "0.5.5",
|
|
||||||
"vue-meta": "2.4.0",
|
|
||||||
"vue-router": "3.4.9",
|
|
||||||
"vuedraggable": "2.24.3",
|
|
||||||
"vuex": "3.6.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@vue/cli-plugin-babel": "4.4.6",
|
"@types/file-saver": "^2.0.5",
|
||||||
"@vue/cli-plugin-eslint": "4.4.6",
|
"@types/js-cookie": "^3.0.2",
|
||||||
"@vue/cli-service": "4.4.6",
|
"@types/node": "^18.7.15",
|
||||||
"babel-eslint": "10.1.0",
|
"@types/nprogress": "^0.2.0",
|
||||||
"babel-plugin-dynamic-import-node": "2.3.3",
|
"@typescript-eslint/eslint-plugin": "^5.33.1",
|
||||||
"chalk": "4.1.0",
|
"@typescript-eslint/parser": "^5.33.1",
|
||||||
"compression-webpack-plugin": "5.0.2",
|
"@vitejs/plugin-vue": "3.1.0",
|
||||||
"connect": "3.6.6",
|
"@vue/compiler-sfc": "3.2.22",
|
||||||
"eslint": "7.15.0",
|
"autoprefixer": "^10.4.8",
|
||||||
"eslint-plugin-vue": "7.2.0",
|
"eslint": "^8.22.0",
|
||||||
"lint-staged": "10.5.3",
|
"eslint-config-prettier": "^8.5.0",
|
||||||
"runjs": "4.4.2",
|
"eslint-plugin-vue": "^9.3.0",
|
||||||
"sass": "1.32.13",
|
"fast-glob": "^3.3.1",
|
||||||
"sass-loader": "10.1.1",
|
"postcss": "^8.4.16",
|
||||||
"script-ext-html-webpack-plugin": "2.1.5",
|
"sass": "1.56.1",
|
||||||
"svg-sprite-loader": "5.1.1",
|
"tailwindcss": "^3.1.8",
|
||||||
"vue-template-compiler": "2.6.12"
|
"ts-node": "^10.9.1",
|
||||||
|
"tsconfig-paths": "^4.1.0",
|
||||||
|
"typescript": "^4.8.2",
|
||||||
|
"unplugin-auto-import": "0.11.4",
|
||||||
|
"unplugin-vue-components": "^0.22.4",
|
||||||
|
"vite": "3.2.7",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
|
"vite-plugin-vue-setup-extend": "^0.4.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.9",
|
"node": ">=8.9",
|
||||||
|
@ -1,28 +1,3 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<router-view />
|
||||||
<router-view />
|
|
||||||
<theme-picker />
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import ThemePicker from "@/components/ThemePicker";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: "App",
|
|
||||||
components: { ThemePicker },
|
|
||||||
metaInfo() {
|
|
||||||
return {
|
|
||||||
title: this.$store.state.settings.dynamicTitle && this.$store.state.settings.title,
|
|
||||||
titleTemplate: title => {
|
|
||||||
return title ? `${title} - ${process.env.VUE_APP_TITLE}` : process.env.VUE_APP_TITLE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<style scoped>
|
|
||||||
#app .theme-picker {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
import request from '@/utils/request'
|
|
||||||
|
|
||||||
// 查询定时任务调度列表
|
|
||||||
export function listJob(query) {
|
|
||||||
return request({
|
|
||||||
url: '/monitor/job/list',
|
|
||||||
method: 'get',
|
|
||||||
params: query
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询定时任务调度详细
|
|
||||||
export function getJob(jobId) {
|
|
||||||
return request({
|
|
||||||
url: '/monitor/job/' + jobId,
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增定时任务调度
|
|
||||||
export function addJob(data) {
|
|
||||||
return request({
|
|
||||||
url: '/monitor/job',
|
|
||||||
method: 'post',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 修改定时任务调度
|
|
||||||
export function updateJob(data) {
|
|
||||||
return request({
|
|
||||||
url: '/monitor/job',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除定时任务调度
|
|
||||||
export function delJob(jobId) {
|
|
||||||
return request({
|
|
||||||
url: '/monitor/job/' + jobId,
|
|
||||||
method: 'delete'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 任务状态修改
|
|
||||||
export function changeJobStatus(jobId, status) {
|
|
||||||
const data = {
|
|
||||||
jobId,
|
|
||||||
status
|
|
||||||
}
|
|
||||||
return request({
|
|
||||||
url: '/monitor/job/changeStatus',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 定时任务立即执行一次
|
|
||||||
export function runJob(jobId, jobGroup) {
|
|
||||||
const data = {
|
|
||||||
jobId,
|
|
||||||
jobGroup
|
|
||||||
}
|
|
||||||
return request({
|
|
||||||
url: '/monitor/job/run',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
import request from '@/utils/request'
|
|
||||||
|
|
||||||
// 获取服务信息
|
|
||||||
export function getServer() {
|
|
||||||
return request({
|
|
||||||
url: '/monitor/server',
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
|
1
ruoyi-ui/src/assets/icons/svg/company.svg
Normal file
1
ruoyi-ui/src/assets/icons/svg/company.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1675914273096" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2417" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1001.7 969.6H890.4V399.4c0-27.7-17.4-52.8-43.3-62.5L580 236.7c-14.9-5.6-31.1-5.5-45.7-0.4V76.6c0-21.9-10.7-42.4-28.7-54.9s-41-15.3-61.5-7.6L176.9 114.3c-25.9 9.7-43.3 34.9-43.3 62.5v792.8H22.3C10 969.6 0 979.6 0 991.9s10 22.3 22.3 22.3H1001.8c12.3 0 22.3-10 22.3-22.3s-10.1-22.3-22.4-22.3zM178.1 176.8c0-9.2 5.8-17.6 14.4-20.8L459.7 55.8c7-2.6 14.4-1.7 20.5 2.5s9.6 10.9 9.6 18.3v893H178.1V176.8z m356.2 792.8V299.3c0-7.4 3.5-14.1 9.6-18.3 6.1-4.2 13.6-5.2 20.5-2.5l267.1 100.2c8.6 3.2 14.4 11.6 14.4 20.8v570.2H534.3z" p-id="2418"></path><path d="M391.8 346.3H258.2c-12.3 0-22.3 10-22.3 22.3s10 22.3 22.3 22.3h133.6c12.3 0 22.3-10 22.3-22.3s-10-22.3-22.3-22.3zM748 479.9H614.4c-12.3 0-22.3 10-22.3 22.3s10 22.3 22.3 22.3H748c12.3 0 22.3-10 22.3-22.3s-10-22.3-22.3-22.3zM748 613.4H614.4c-12.3 0-22.3 10-22.3 22.3s10 22.3 22.3 22.3H748c12.3 0 22.3-10 22.3-22.3s-10-22.3-22.3-22.3zM391.8 613.4H258.2c-12.3 0-22.3 10-22.3 22.3s10 22.3 22.3 22.3h133.6c12.3 0 22.3-10 22.3-22.3s-10-22.3-22.3-22.3z" p-id="2419"></path></svg>
|
After Width: | Height: | Size: 1.3 KiB |
1
ruoyi-ui/src/assets/icons/svg/gitee.svg
Normal file
1
ruoyi-ui/src/assets/icons/svg/gitee.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1686919908144" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2521" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 992C246.895625 992 32 777.104375 32 512S246.895625 32 512 32s480 214.895625 480 480-214.895625 480-480 480z m242.9521875-533.3278125h-272.56875a23.7121875 23.7121875 0 0 0-23.71125 23.7121875l-0.024375 59.255625c0 13.08 10.6078125 23.7121875 23.6878125 23.7121875h165.96c13.104375 0 23.7121875 10.6078125 23.7121875 23.6878125v11.855625a71.1121875 71.1121875 0 0 1-71.1121875 71.1121875h-225.215625a23.7121875 23.7121875 0 0 1-23.6878125-23.7121875V423.1278125a71.1121875 71.1121875 0 0 1 71.0878125-71.1121875h331.824375a23.7121875 23.7121875 0 0 0 23.6878125-23.71125l0.0721875-59.2565625a23.7121875 23.7121875 0 0 0-23.68875-23.7121875H423.08a177.76875 177.76875 0 0 0-177.76875 177.7921875V754.953125c0 13.1034375 10.60875 23.7121875 23.713125 23.7121875h349.63125a159.984375 159.984375 0 0 0 159.984375-159.984375V482.36a23.7121875 23.7121875 0 0 0-23.7121875-23.6878125z" fill="#515151" p-id="2522"></path></svg>
|
After Width: | Height: | Size: 1.2 KiB |
3
ruoyi-ui/src/assets/icons/svg/maxkey.svg
Normal file
3
ruoyi-ui/src/assets/icons/svg/maxkey.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 9.3 KiB |
65
ruoyi-ui/src/assets/styles/variables.module.scss
Normal file
65
ruoyi-ui/src/assets/styles/variables.module.scss
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// base color
|
||||||
|
$blue: #324157;
|
||||||
|
$light-blue: #3A71A8;
|
||||||
|
$red: #C03639;
|
||||||
|
$pink: #E65D6E;
|
||||||
|
$green: #30B08F;
|
||||||
|
$tiffany: #4AB7BD;
|
||||||
|
$yellow: #FEC171;
|
||||||
|
$panGreen: #30B08F;
|
||||||
|
|
||||||
|
// 默认菜单主题风格
|
||||||
|
$base-menu-color: #bfcbd9;
|
||||||
|
$base-menu-color-active: #f4f4f5;
|
||||||
|
$base-menu-background: #304156;
|
||||||
|
$base-logo-title-color: #ffffff;
|
||||||
|
|
||||||
|
$base-menu-light-color: rgba(0, 0, 0, 0.7);
|
||||||
|
$base-menu-light-background: #ffffff;
|
||||||
|
$base-logo-light-title-color: #001529;
|
||||||
|
|
||||||
|
$base-sub-menu-background: #1f2d3d;
|
||||||
|
$base-sub-menu-hover: #001528;
|
||||||
|
|
||||||
|
// 自定义暗色菜单风格
|
||||||
|
/**
|
||||||
|
$base-menu-color:hsla(0,0%,100%,.65);
|
||||||
|
$base-menu-color-active:#fff;
|
||||||
|
$base-menu-background:#001529;
|
||||||
|
$base-logo-title-color: #ffffff;
|
||||||
|
|
||||||
|
$base-menu-light-color:rgba(0,0,0,.70);
|
||||||
|
$base-menu-light-background:#ffffff;
|
||||||
|
$base-logo-light-title-color: #001529;
|
||||||
|
|
||||||
|
$base-sub-menu-background:#000c17;
|
||||||
|
$base-sub-menu-hover:#001528;
|
||||||
|
*/
|
||||||
|
|
||||||
|
$--color-primary: #409EFF;
|
||||||
|
$--color-success: #67C23A;
|
||||||
|
$--color-warning: #E6A23C;
|
||||||
|
$--color-danger: #F56C6C;
|
||||||
|
$--color-info: #909399;
|
||||||
|
|
||||||
|
$base-sidebar-width: 200px;
|
||||||
|
|
||||||
|
// the :export directive is the magic sauce for webpack
|
||||||
|
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
|
||||||
|
:export {
|
||||||
|
menuColor: $base-menu-color;
|
||||||
|
menuLightColor: $base-menu-light-color;
|
||||||
|
menuColorActive: $base-menu-color-active;
|
||||||
|
menuBackground: $base-menu-background;
|
||||||
|
menuLightBackground: $base-menu-light-background;
|
||||||
|
subMenuBackground: $base-sub-menu-background;
|
||||||
|
subMenuHover: $base-sub-menu-hover;
|
||||||
|
sideBarWidth: $base-sidebar-width;
|
||||||
|
logoTitleColor: $base-logo-title-color;
|
||||||
|
logoLightTitleColor: $base-logo-light-title-color;
|
||||||
|
primaryColor: $--color-primary;
|
||||||
|
successColor: $--color-success;
|
||||||
|
dangerColor: $--color-danger;
|
||||||
|
infoColor: $--color-info;
|
||||||
|
warningColor: $--color-warning;
|
||||||
|
}
|
@ -9,54 +9,46 @@
|
|||||||
</el-breadcrumb>
|
</el-breadcrumb>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
const route = useRoute();
|
||||||
data() {
|
const router = useRouter();
|
||||||
return {
|
const levelList = ref([])
|
||||||
levelList: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
$route(route) {
|
|
||||||
// if you go to the redirect page, do not update the breadcrumbs
|
|
||||||
if (route.path.startsWith('/redirect/')) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.getBreadcrumb()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
this.getBreadcrumb()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
getBreadcrumb() {
|
|
||||||
// only show routes with meta.title
|
|
||||||
let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
|
|
||||||
const first = matched[0]
|
|
||||||
|
|
||||||
if (!this.isDashboard(first)) {
|
function getBreadcrumb() {
|
||||||
matched = [{ path: '/index', meta: { title: '首页' }}].concat(matched)
|
// only show routes with meta.title
|
||||||
}
|
let matched = route.matched.filter(item => item.meta && item.meta.title);
|
||||||
|
const first = matched[0]
|
||||||
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
// 判断是否为首页
|
||||||
},
|
if (!isDashboard(first)) {
|
||||||
isDashboard(route) {
|
matched = [{ path: '/index', meta: { title: '首页' } }].concat(matched)
|
||||||
const name = route && route.name
|
|
||||||
if (!name) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return name.trim() === 'Index'
|
|
||||||
},
|
|
||||||
handleLink(item) {
|
|
||||||
const { redirect, path } = item
|
|
||||||
if (redirect) {
|
|
||||||
this.$router.push(redirect)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.$router.push(path)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
|
||||||
}
|
}
|
||||||
|
function isDashboard(route) {
|
||||||
|
const name = route && route.name
|
||||||
|
if (!name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return name.trim() === 'Index'
|
||||||
|
}
|
||||||
|
function handleLink(item) {
|
||||||
|
const { redirect, path } = item
|
||||||
|
if (redirect) {
|
||||||
|
router.push(redirect)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
// if you go to the redirect page, do not update the breadcrumbs
|
||||||
|
if (route.path.startsWith('/redirect/')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
getBreadcrumb()
|
||||||
|
})
|
||||||
|
getBreadcrumb();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -182,7 +182,6 @@ export default {
|
|||||||
"updateCrontabValue", name, value, from;
|
"updateCrontabValue", name, value, from;
|
||||||
this.crontabValueObj[name] = value;
|
this.crontabValueObj[name] = value;
|
||||||
if (from && from !== name) {
|
if (from && from !== name) {
|
||||||
console.log(`来自组件 ${from} 改变了 ${name} ${value}`);
|
|
||||||
this.changeRadio(name, value);
|
this.changeRadio(name, value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3,88 +3,45 @@
|
|||||||
<template v-for="(item, index) in options">
|
<template v-for="(item, index) in options">
|
||||||
<template v-if="values.includes(item.value)">
|
<template v-if="values.includes(item.value)">
|
||||||
<span
|
<span
|
||||||
v-if="item.raw.listClass == 'default' || item.raw.listClass == ''"
|
v-if="item.elTagType == 'default' || item.elTagType == ''"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:index="index"
|
:index="index"
|
||||||
:class="item.raw.cssClass"
|
:class="item.elTagType"
|
||||||
>{{ item.label + " " }}</span
|
>{{ item.label }}</span>
|
||||||
>
|
|
||||||
<el-tag
|
<el-tag
|
||||||
v-else
|
v-else
|
||||||
:disable-transitions="true"
|
:disable-transitions="true"
|
||||||
:key="item.value"
|
:key="item.value + ''"
|
||||||
:index="index"
|
:index="index"
|
||||||
:type="item.raw.listClass == 'primary' ? '' : item.raw.listClass"
|
:type="item.elTagType === 'primary' ? '' : item.elTagType"
|
||||||
:class="item.raw.cssClass"
|
:class="item.elTagType"
|
||||||
>
|
>{{ item.label }}</el-tag>
|
||||||
{{ item.label + " " }}
|
|
||||||
</el-tag>
|
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="unmatch && showValue">
|
|
||||||
{{ unmatchArray | handleArray }}
|
|
||||||
</template>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: "DictTag",
|
// 数据
|
||||||
props: {
|
options: {
|
||||||
options: {
|
type: Array,
|
||||||
type: Array,
|
default: null,
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
value: [Number, String, Array],
|
|
||||||
// 当未找到匹配的数据时,显示value
|
|
||||||
showValue: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
// 当前的值
|
||||||
return {
|
value: [Number, String, Array],
|
||||||
unmatchArray: [], // 记录未匹配的项
|
})
|
||||||
}
|
|
||||||
},
|
const values = computed(() => {
|
||||||
computed: {
|
if (props.value !== null && typeof props.value !== 'undefined') {
|
||||||
values() {
|
return Array.isArray(props.value) ? props.value : [String(props.value)];
|
||||||
if (this.value !== null && typeof this.value !== "undefined") {
|
} else {
|
||||||
return Array.isArray(this.value) ? this.value : [String(this.value)];
|
return [];
|
||||||
} else {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
unmatch() {
|
|
||||||
this.unmatchArray = [];
|
|
||||||
if (this.value !== null && typeof this.value !== "undefined") {
|
|
||||||
// 传入值为非数组
|
|
||||||
if (!Array.isArray(this.value)) {
|
|
||||||
if (this.options.some((v) => v.value == this.value)) return false;
|
|
||||||
this.unmatchArray.push(this.value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 传入值为Array
|
|
||||||
this.value.forEach((item) => {
|
|
||||||
if (!this.options.some((v) => v.value == item))
|
|
||||||
this.unmatchArray.push(item);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 没有value不显示
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
filters: {
|
|
||||||
handleArray(array) {
|
|
||||||
if (array.length === 0) return "";
|
|
||||||
return array.reduce((pre, cur) => {
|
|
||||||
return pre + " " + cur;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.el-tag + .el-tag {
|
.el-tag + .el-tag {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="upload-file">
|
<div class="upload-file">
|
||||||
<el-upload
|
<el-upload
|
||||||
multiple
|
|
||||||
:action="uploadFileUrl"
|
:action="uploadFileUrl"
|
||||||
:before-upload="handleBeforeUpload"
|
:before-upload="handleBeforeUpload"
|
||||||
:file-list="fileList"
|
:file-list="fileList"
|
||||||
@ -12,12 +11,12 @@
|
|||||||
:show-file-list="false"
|
:show-file-list="false"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
class="upload-file-uploader"
|
class="upload-file-uploader"
|
||||||
ref="fileUpload"
|
ref="upload"
|
||||||
>
|
>
|
||||||
<!-- 上传按钮 -->
|
<!-- 上传按钮 -->
|
||||||
<el-button size="mini" type="primary">选取文件</el-button>
|
<el-button type="primary">选取文件</el-button>
|
||||||
<!-- 上传提示 -->
|
<!-- 上传提示 -->
|
||||||
<div class="el-upload__tip" slot="tip" v-if="showTip">
|
<div class="el-upload__tip" v-if="showTip">
|
||||||
请上传
|
请上传
|
||||||
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
||||||
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
||||||
@ -27,7 +26,7 @@
|
|||||||
|
|
||||||
<!-- 文件列表 -->
|
<!-- 文件列表 -->
|
||||||
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
|
||||||
<li :key="file.url" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
|
||||||
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
|
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
|
||||||
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
<span class="el-icon-document"> {{ getFileName(file.name) }} </span>
|
||||||
</el-link>
|
</el-link>
|
||||||
@ -39,158 +38,132 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { getToken } from "@/utils/auth";
|
import { getToken } from "@/utils/auth";
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: "FileUpload",
|
modelValue: [String, Object, Array],
|
||||||
props: {
|
// 数量限制
|
||||||
// 值
|
limit: {
|
||||||
value: [String, Object, Array],
|
type: Number,
|
||||||
// 数量限制
|
default: 5,
|
||||||
limit: {
|
},
|
||||||
type: Number,
|
// 大小限制(MB)
|
||||||
default: 5,
|
fileSize: {
|
||||||
},
|
type: Number,
|
||||||
// 大小限制(MB)
|
default: 5,
|
||||||
fileSize: {
|
},
|
||||||
type: Number,
|
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||||
default: 5,
|
fileType: {
|
||||||
},
|
type: Array,
|
||||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
|
||||||
fileType: {
|
},
|
||||||
type: Array,
|
// 是否显示提示
|
||||||
default: () => ["doc", "xls", "ppt", "txt", "pdf"],
|
isShowTip: {
|
||||||
},
|
type: Boolean,
|
||||||
// 是否显示提示
|
default: true
|
||||||
isShowTip: {
|
}
|
||||||
type: Boolean,
|
});
|
||||||
default: true
|
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
const emit = defineEmits();
|
||||||
|
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||||
|
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
||||||
|
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||||
|
const fileList = ref([]);
|
||||||
|
const showTip = computed(
|
||||||
|
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(() => props.modelValue, val => {
|
||||||
|
if (val) {
|
||||||
|
let temp = 1;
|
||||||
|
// 首先将值转为数组
|
||||||
|
const list = Array.isArray(val) ? val : props.modelValue.split(',');
|
||||||
|
// 然后将数组转为对象数组
|
||||||
|
fileList.value = list.map(item => {
|
||||||
|
if (typeof item === "string") {
|
||||||
|
item = { name: item, url: item };
|
||||||
|
}
|
||||||
|
item.uid = item.uid || new Date().getTime() + temp++;
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fileList.value = [];
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 上传前校检格式和大小
|
||||||
|
function handleBeforeUpload(file) {
|
||||||
|
// 校检文件类型
|
||||||
|
if (props.fileType.length) {
|
||||||
|
let fileExtension = "";
|
||||||
|
if (file.name.lastIndexOf(".") > -1) {
|
||||||
|
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
||||||
}
|
}
|
||||||
},
|
const isTypeOk = props.fileType.some((type) => {
|
||||||
data() {
|
if (file.type.indexOf(type) > -1) return true;
|
||||||
return {
|
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
||||||
number: 0,
|
return false;
|
||||||
uploadList: [],
|
});
|
||||||
baseUrl: process.env.VUE_APP_BASE_API,
|
if (!isTypeOk) {
|
||||||
uploadFileUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传文件服务器地址
|
proxy.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`);
|
||||||
headers: {
|
return false;
|
||||||
Authorization: "Bearer " + getToken(),
|
|
||||||
},
|
|
||||||
fileList: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
value: {
|
|
||||||
handler(val) {
|
|
||||||
if (val) {
|
|
||||||
let temp = 1;
|
|
||||||
// 首先将值转为数组
|
|
||||||
const list = Array.isArray(val) ? val : this.value.split(',');
|
|
||||||
// 然后将数组转为对象数组
|
|
||||||
this.fileList = list.map(item => {
|
|
||||||
if (typeof item === "string") {
|
|
||||||
item = { name: item, url: item };
|
|
||||||
}
|
|
||||||
item.uid = item.uid || new Date().getTime() + temp++;
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.fileList = [];
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
// 是否显示提示
|
|
||||||
showTip() {
|
|
||||||
return this.isShowTip && (this.fileType || this.fileSize);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 上传前校检格式和大小
|
|
||||||
handleBeforeUpload(file) {
|
|
||||||
// 校检文件类型
|
|
||||||
if (this.fileType) {
|
|
||||||
const fileName = file.name.split('.');
|
|
||||||
const fileExt = fileName[fileName.length - 1];
|
|
||||||
const isTypeOk = this.fileType.indexOf(fileExt) >= 0;
|
|
||||||
if (!isTypeOk) {
|
|
||||||
this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 校检文件大小
|
|
||||||
if (this.fileSize) {
|
|
||||||
const isLt = file.size / 1024 / 1024 < this.fileSize;
|
|
||||||
if (!isLt) {
|
|
||||||
this.$modal.msgError(`上传文件大小不能超过 ${this.fileSize} MB!`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.$modal.loading("正在上传文件,请稍候...");
|
|
||||||
this.number++;
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
// 文件个数超出
|
|
||||||
handleExceed() {
|
|
||||||
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
|
|
||||||
},
|
|
||||||
// 上传失败
|
|
||||||
handleUploadError(err) {
|
|
||||||
this.$modal.msgError("上传文件失败,请重试");
|
|
||||||
this.$modal.closeLoading()
|
|
||||||
},
|
|
||||||
// 上传成功回调
|
|
||||||
handleUploadSuccess(res, file) {
|
|
||||||
if (res.code === 200) {
|
|
||||||
this.uploadList.push({ name: res.fileName, url: res.fileName });
|
|
||||||
this.uploadedSuccessfully();
|
|
||||||
} else {
|
|
||||||
this.number--;
|
|
||||||
this.$modal.closeLoading();
|
|
||||||
this.$modal.msgError(res.msg);
|
|
||||||
this.$refs.fileUpload.handleRemove(file);
|
|
||||||
this.uploadedSuccessfully();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 删除文件
|
|
||||||
handleDelete(index) {
|
|
||||||
this.fileList.splice(index, 1);
|
|
||||||
this.$emit("input", this.listToString(this.fileList));
|
|
||||||
},
|
|
||||||
// 上传结束处理
|
|
||||||
uploadedSuccessfully() {
|
|
||||||
if (this.number > 0 && this.uploadList.length === this.number) {
|
|
||||||
this.fileList = this.fileList.concat(this.uploadList);
|
|
||||||
this.uploadList = [];
|
|
||||||
this.number = 0;
|
|
||||||
this.$emit("input", this.listToString(this.fileList));
|
|
||||||
this.$modal.closeLoading();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 获取文件名称
|
|
||||||
getFileName(name) {
|
|
||||||
if (name.lastIndexOf("/") > -1) {
|
|
||||||
return name.slice(name.lastIndexOf("/") + 1);
|
|
||||||
} else {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 对象转成指定字符串分隔
|
|
||||||
listToString(list, separator) {
|
|
||||||
let strs = "";
|
|
||||||
separator = separator || ",";
|
|
||||||
for (let i in list) {
|
|
||||||
strs += list[i].url + separator;
|
|
||||||
}
|
|
||||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
// 校检文件大小
|
||||||
|
if (props.fileSize) {
|
||||||
|
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||||
|
if (!isLt) {
|
||||||
|
proxy.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件个数超出
|
||||||
|
function handleExceed() {
|
||||||
|
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传失败
|
||||||
|
function handleUploadError(err) {
|
||||||
|
proxy.$modal.msgError("上传失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传成功回调
|
||||||
|
function handleUploadSuccess(res, file) {
|
||||||
|
proxy.$modal.msgSuccess("上传成功");
|
||||||
|
fileList.value.push({ name: res.fileName, url: res.fileName });
|
||||||
|
emit("update:modelValue", listToString(fileList.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除文件
|
||||||
|
function handleDelete(index) {
|
||||||
|
fileList.value.splice(index, 1);
|
||||||
|
emit("update:modelValue", listToString(fileList.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取文件名称
|
||||||
|
function getFileName(name) {
|
||||||
|
if (name.lastIndexOf("/") > -1) {
|
||||||
|
return name.slice(name.lastIndexOf("/") + 1).toLowerCase();
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对象转成指定字符串分隔
|
||||||
|
function listToString(list, separator) {
|
||||||
|
let strs = "";
|
||||||
|
separator = separator || ",";
|
||||||
|
for (let i in list) {
|
||||||
|
strs += list[i].url + separator;
|
||||||
|
}
|
||||||
|
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@ -13,20 +13,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
defineProps({
|
||||||
name: 'Hamburger',
|
isActive: {
|
||||||
props: {
|
type: Boolean,
|
||||||
isActive: {
|
default: false
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleClick() {
|
|
||||||
this.$emit('toggleClick')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits()
|
||||||
|
const toggleClick = () => {
|
||||||
|
emit('toggleClick');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{'show':show}" class="header-search">
|
<div :class="{ 'show': show }" class="header-search">
|
||||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
||||||
<el-select
|
<el-select
|
||||||
ref="headerSearchSelect"
|
ref="headerSearchSelectRef"
|
||||||
v-model="search"
|
v-model="search"
|
||||||
:remote-method="querySearch"
|
:remote-method="querySearch"
|
||||||
filterable
|
filterable
|
||||||
@ -17,138 +17,129 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
// fuse is a lightweight fuzzy-search module
|
import Fuse from 'fuse.js'
|
||||||
// make search results more in line with expectations
|
import { getNormalPath } from '@/utils/ruoyi'
|
||||||
import Fuse from 'fuse.js/dist/fuse.min.js'
|
import { isHttp } from '@/utils/validate'
|
||||||
import path from 'path'
|
|
||||||
|
|
||||||
export default {
|
const search = ref('');
|
||||||
name: 'HeaderSearch',
|
const options = ref([]);
|
||||||
data() {
|
const searchPool = ref([]);
|
||||||
return {
|
const show = ref(false);
|
||||||
search: '',
|
const fuse = ref(undefined);
|
||||||
options: [],
|
const headerSearchSelectRef = ref(null);
|
||||||
searchPool: [],
|
const store = useStore();
|
||||||
show: false,
|
const router = useRouter();
|
||||||
fuse: undefined
|
const routes = computed(() => store.getters.permission_routes);
|
||||||
|
|
||||||
|
function click() {
|
||||||
|
show.value = !show.value
|
||||||
|
if (show.value) {
|
||||||
|
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function close() {
|
||||||
|
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
||||||
|
options.value = []
|
||||||
|
show.value = false
|
||||||
|
}
|
||||||
|
function change(val) {
|
||||||
|
const path = val.path;
|
||||||
|
if (isHttp(path)) {
|
||||||
|
// http(s):// 路径新窗口打开
|
||||||
|
const pindex = path.indexOf("http");
|
||||||
|
window.open(path.substr(pindex, path.length), "_blank");
|
||||||
|
} else {
|
||||||
|
router.push(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
search.value = ''
|
||||||
|
options.value = []
|
||||||
|
nextTick(() => {
|
||||||
|
show.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function initFuse(list) {
|
||||||
|
fuse.value = new Fuse(list, {
|
||||||
|
shouldSort: true,
|
||||||
|
threshold: 0.4,
|
||||||
|
location: 0,
|
||||||
|
distance: 100,
|
||||||
|
maxPatternLength: 32,
|
||||||
|
minMatchCharLength: 1,
|
||||||
|
keys: [{
|
||||||
|
name: 'title',
|
||||||
|
weight: 0.7
|
||||||
|
}, {
|
||||||
|
name: 'path',
|
||||||
|
weight: 0.3
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Filter out the routes that can be displayed in the sidebar
|
||||||
|
// And generate the internationalized title
|
||||||
|
function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||||
|
let res = []
|
||||||
|
|
||||||
|
for (const r of routes) {
|
||||||
|
// skip hidden router
|
||||||
|
if (r.hidden) { continue }
|
||||||
|
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
||||||
|
const data = {
|
||||||
|
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||||
|
title: [...prefixTitle]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
computed: {
|
if (r.meta && r.meta.title) {
|
||||||
routes() {
|
data.title = [...data.title, r.meta.title]
|
||||||
return this.$store.getters.permission_routes
|
|
||||||
}
|
if (r.redirect !== 'noRedirect') {
|
||||||
},
|
// only push the routes with title
|
||||||
watch: {
|
// special case: need to exclude parent router without redirect
|
||||||
routes() {
|
res.push(data)
|
||||||
this.searchPool = this.generateRoutes(this.routes)
|
|
||||||
},
|
|
||||||
searchPool(list) {
|
|
||||||
this.initFuse(list)
|
|
||||||
},
|
|
||||||
show(value) {
|
|
||||||
if (value) {
|
|
||||||
document.body.addEventListener('click', this.close)
|
|
||||||
} else {
|
|
||||||
document.body.removeEventListener('click', this.close)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
mounted() {
|
// recursive child routes
|
||||||
this.searchPool = this.generateRoutes(this.routes)
|
if (r.children) {
|
||||||
},
|
const tempRoutes = generateRoutes(r.children, data.path, data.title)
|
||||||
methods: {
|
if (tempRoutes.length >= 1) {
|
||||||
click() {
|
res = [...res, ...tempRoutes]
|
||||||
this.show = !this.show
|
|
||||||
if (this.show) {
|
|
||||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.focus()
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
close() {
|
|
||||||
this.$refs.headerSearchSelect && this.$refs.headerSearchSelect.blur()
|
|
||||||
this.options = []
|
|
||||||
this.show = false
|
|
||||||
},
|
|
||||||
change(val) {
|
|
||||||
const path = val.path;
|
|
||||||
if(this.ishttp(val.path)) {
|
|
||||||
// http(s):// 路径新窗口打开
|
|
||||||
const pindex = path.indexOf("http");
|
|
||||||
window.open(path.substr(pindex, path.length), "_blank");
|
|
||||||
} else {
|
|
||||||
this.$router.push(val.path)
|
|
||||||
}
|
|
||||||
this.search = ''
|
|
||||||
this.options = []
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.show = false
|
|
||||||
})
|
|
||||||
},
|
|
||||||
initFuse(list) {
|
|
||||||
this.fuse = new Fuse(list, {
|
|
||||||
shouldSort: true,
|
|
||||||
threshold: 0.4,
|
|
||||||
location: 0,
|
|
||||||
distance: 100,
|
|
||||||
minMatchCharLength: 1,
|
|
||||||
keys: [{
|
|
||||||
name: 'title',
|
|
||||||
weight: 0.7
|
|
||||||
}, {
|
|
||||||
name: 'path',
|
|
||||||
weight: 0.3
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
},
|
|
||||||
// Filter out the routes that can be displayed in the sidebar
|
|
||||||
// And generate the internationalized title
|
|
||||||
generateRoutes(routes, basePath = '/', prefixTitle = []) {
|
|
||||||
let res = []
|
|
||||||
|
|
||||||
for (const router of routes) {
|
|
||||||
// skip hidden router
|
|
||||||
if (router.hidden) { continue }
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
path: !this.ishttp(router.path) ? path.resolve(basePath, router.path) : router.path,
|
|
||||||
title: [...prefixTitle]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (router.meta && router.meta.title) {
|
|
||||||
data.title = [...data.title, router.meta.title]
|
|
||||||
|
|
||||||
if (router.redirect !== 'noRedirect') {
|
|
||||||
// only push the routes with title
|
|
||||||
// special case: need to exclude parent router without redirect
|
|
||||||
res.push(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// recursive child routes
|
|
||||||
if (router.children) {
|
|
||||||
const tempRoutes = this.generateRoutes(router.children, data.path, data.title)
|
|
||||||
if (tempRoutes.length >= 1) {
|
|
||||||
res = [...res, ...tempRoutes]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
querySearch(query) {
|
|
||||||
if (query !== '') {
|
|
||||||
this.options = this.fuse.search(query)
|
|
||||||
} else {
|
|
||||||
this.options = []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ishttp(url) {
|
|
||||||
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
function querySearch(query) {
|
||||||
|
if (query !== '') {
|
||||||
|
options.value = fuse.value.search(query)
|
||||||
|
} else {
|
||||||
|
options.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
searchPool.value = generateRoutes(routes.value);
|
||||||
|
})
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
searchPool.value = generateRoutes(routes.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(show, (value) => {
|
||||||
|
if (value) {
|
||||||
|
document.body.addEventListener('click', close)
|
||||||
|
} else {
|
||||||
|
document.body.removeEventListener('click', close)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(searchPool, (list) => {
|
||||||
|
initFuse(list)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang='scss' scoped>
|
||||||
.header-search {
|
.header-search {
|
||||||
font-size: 0 !important;
|
font-size: 0 !important;
|
||||||
|
|
||||||
@ -168,7 +159,7 @@ export default {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
|
||||||
::v-deep .el-input__inner {
|
:deep(.el-input__inner) {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
@ -1,104 +1,74 @@
|
|||||||
<!-- @author zhengjie -->
|
|
||||||
<template>
|
<template>
|
||||||
<div class="icon-body">
|
<div class="icon-body">
|
||||||
<el-input v-model="name" class="icon-search" clearable placeholder="请输入图标名称" @clear="filterIcons" @input="filterIcons">
|
<el-input
|
||||||
<i slot="suffix" class="el-icon-search el-input__icon" />
|
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>
|
</el-input>
|
||||||
<div class="icon-list">
|
<div class="icon-list">
|
||||||
<div class="list-container">
|
<div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
|
||||||
<div v-for="(item, index) in iconList" class="icon-item-wrapper" :key="index" @click="selectedIcon(item)">
|
<svg-icon :icon-class="item" style="height: 30px;width: 16px;" />
|
||||||
<div :class="['icon-item', { active: activeIcon === item }]">
|
<span>{{ item }}</span>
|
||||||
<svg-icon :icon-class="item" class-name="icon" style="height: 25px;width: 16px;"/>
|
|
||||||
<span>{{ item }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import icons from './requireIcons'
|
import icons from './requireIcons'
|
||||||
export default {
|
|
||||||
name: 'IconSelect',
|
const iconName = ref('');
|
||||||
props: {
|
const iconList = ref(icons);
|
||||||
activeIcon: {
|
const emit = defineEmits(['selected']);
|
||||||
type: String
|
|
||||||
|
function filterIcons() {
|
||||||
|
iconList.value = icons
|
||||||
|
if (iconName.value) {
|
||||||
|
iconList.value = icons.filter(item => item.indexOf(iconName.value) !== -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedIcon(name) {
|
||||||
|
emit('selected', name)
|
||||||
|
document.body.click()
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
iconName.value = ''
|
||||||
|
iconList.value = icons
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
reset
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.icon-body {
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px;
|
||||||
|
.icon-list {
|
||||||
|
height: 200px;
|
||||||
|
overflow-y: scroll;
|
||||||
|
div {
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
margin-bottom: -5px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 33%;
|
||||||
|
float: left;
|
||||||
}
|
}
|
||||||
},
|
span {
|
||||||
data() {
|
display: inline-block;
|
||||||
return {
|
vertical-align: -0.15em;
|
||||||
name: '',
|
fill: currentColor;
|
||||||
iconList: icons
|
overflow: hidden;
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
filterIcons() {
|
|
||||||
this.iconList = icons
|
|
||||||
if (this.name) {
|
|
||||||
this.iconList = this.iconList.filter(item => item.includes(this.name))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedIcon(name) {
|
|
||||||
this.$emit('selected', name)
|
|
||||||
document.body.click()
|
|
||||||
},
|
|
||||||
reset() {
|
|
||||||
this.name = ''
|
|
||||||
this.iconList = icons
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
<style rel="stylesheet/scss" lang="scss" scoped>
|
|
||||||
.icon-body {
|
|
||||||
width: 100%;
|
|
||||||
padding: 10px;
|
|
||||||
.icon-search {
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
.icon-list {
|
|
||||||
height: 200px;
|
|
||||||
overflow: auto;
|
|
||||||
.list-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
.icon-item-wrapper {
|
|
||||||
width: calc(100% / 3);
|
|
||||||
height: 25px;
|
|
||||||
line-height: 25px;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
.icon-item {
|
|
||||||
display: flex;
|
|
||||||
max-width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0 5px;
|
|
||||||
&:hover {
|
|
||||||
background: #ececec;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
.icon {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: -0.15em;
|
|
||||||
fill: currentColor;
|
|
||||||
padding-left: 2px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.icon-item.active {
|
|
||||||
background: #ececec;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
|
let icons = []
|
||||||
const req = require.context('../../assets/icons/svg', false, /\.svg$/)
|
const modules = import.meta.glob('./../../assets/icons/svg/*.svg');
|
||||||
const requireAll = requireContext => requireContext.keys()
|
for (const path in modules) {
|
||||||
|
const p = path.split('assets/icons/svg/')[1].split('.svg')[0];
|
||||||
const re = /\.\/(.*)\.svg/
|
icons.push(p);
|
||||||
|
}
|
||||||
const icons = requireAll(req).map(i => {
|
|
||||||
return i.match(re)[1]
|
|
||||||
})
|
|
||||||
|
|
||||||
export default icons
|
export default icons
|
||||||
|
@ -5,64 +5,59 @@
|
|||||||
:style="`width:${realWidth};height:${realHeight};`"
|
:style="`width:${realWidth};height:${realHeight};`"
|
||||||
:preview-src-list="realSrcList"
|
:preview-src-list="realSrcList"
|
||||||
>
|
>
|
||||||
<div slot="error" class="image-slot">
|
<template #error>
|
||||||
<i class="el-icon-picture-outline"></i>
|
<div class="image-slot">
|
||||||
</div>
|
<el-icon><picture-filled /></el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-image>
|
</el-image>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { isExternal } from "@/utils/validate";
|
import { isExternal } from "@/utils/validate";
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: "ImagePreview",
|
src: {
|
||||||
props: {
|
type: String,
|
||||||
src: {
|
required: true
|
||||||
type: String,
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: [Number, String],
|
|
||||||
default: ""
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: [Number, String],
|
|
||||||
default: ""
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
width: {
|
||||||
realSrc() {
|
type: [Number, String],
|
||||||
if (!this.src) {
|
default: ""
|
||||||
return;
|
|
||||||
}
|
|
||||||
let real_src = this.src.split(",")[0];
|
|
||||||
if (isExternal(real_src)) {
|
|
||||||
return real_src;
|
|
||||||
}
|
|
||||||
return process.env.VUE_APP_BASE_API + real_src;
|
|
||||||
},
|
|
||||||
realSrcList() {
|
|
||||||
if (!this.src) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let real_src_list = this.src.split(",");
|
|
||||||
let srcList = [];
|
|
||||||
real_src_list.forEach(item => {
|
|
||||||
if (isExternal(item)) {
|
|
||||||
return srcList.push(item);
|
|
||||||
}
|
|
||||||
return srcList.push(process.env.VUE_APP_BASE_API + item);
|
|
||||||
});
|
|
||||||
return srcList;
|
|
||||||
},
|
|
||||||
realWidth() {
|
|
||||||
return typeof this.width == "string" ? this.width : `${this.width}px`;
|
|
||||||
},
|
|
||||||
realHeight() {
|
|
||||||
return typeof this.height == "string" ? this.height : `${this.height}px`;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
height: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const realSrc = computed(() => {
|
||||||
|
let real_src = props.src.split(",")[0];
|
||||||
|
if (isExternal(real_src)) {
|
||||||
|
return real_src;
|
||||||
|
}
|
||||||
|
return import.meta.env.VITE_APP_BASE_API + real_src;
|
||||||
|
});
|
||||||
|
|
||||||
|
const realSrcList = computed(() => {
|
||||||
|
let real_src_list = props.src.split(",");
|
||||||
|
let srcList = [];
|
||||||
|
real_src_list.forEach(item => {
|
||||||
|
if (isExternal(item)) {
|
||||||
|
return srcList.push(item);
|
||||||
|
}
|
||||||
|
return srcList.push(import.meta.env.VITE_APP_BASE_API + item);
|
||||||
|
});
|
||||||
|
return srcList;
|
||||||
|
});
|
||||||
|
|
||||||
|
const realWidth = computed(() =>
|
||||||
|
typeof props.width == "string" ? props.width : `${props.width}px`
|
||||||
|
);
|
||||||
|
|
||||||
|
const realHeight = computed(() =>
|
||||||
|
typeof props.height == "string" ? props.height : `${props.height}px`
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -70,14 +65,14 @@ export default {
|
|||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
background-color: #ebeef5;
|
background-color: #ebeef5;
|
||||||
box-shadow: 0 0 5px 1px #ccc;
|
box-shadow: 0 0 5px 1px #ccc;
|
||||||
::v-deep .el-image__inner {
|
:deep(.el-image__inner) {
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
&:hover {
|
&:hover {
|
||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
::v-deep .image-slot {
|
:deep(.image-slot) {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="component-upload-image">
|
<div class="component-upload-image">
|
||||||
<el-upload
|
<el-upload
|
||||||
multiple
|
|
||||||
:action="uploadImgUrl"
|
:action="uploadImgUrl"
|
||||||
list-type="picture-card"
|
list-type="picture-card"
|
||||||
:on-success="handleUploadSuccess"
|
:on-success="handleUploadSuccess"
|
||||||
@ -9,29 +8,32 @@
|
|||||||
:limit="limit"
|
:limit="limit"
|
||||||
:on-error="handleUploadError"
|
:on-error="handleUploadError"
|
||||||
:on-exceed="handleExceed"
|
:on-exceed="handleExceed"
|
||||||
ref="imageUpload"
|
name="file"
|
||||||
:on-remove="handleDelete"
|
:on-remove="handleRemove"
|
||||||
:show-file-list="true"
|
:show-file-list="true"
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
:file-list="fileList"
|
:file-list="fileList"
|
||||||
:on-preview="handlePictureCardPreview"
|
:on-preview="handlePictureCardPreview"
|
||||||
:class="{hide: this.fileList.length >= this.limit}"
|
:class="{ hide: fileList.length >= limit }"
|
||||||
>
|
>
|
||||||
<i class="el-icon-plus"></i>
|
<el-icon class="avatar-uploader-icon"><plus /></el-icon>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
|
||||||
<!-- 上传提示 -->
|
<!-- 上传提示 -->
|
||||||
<div class="el-upload__tip" slot="tip" v-if="showTip">
|
<div class="el-upload__tip" v-if="showTip">
|
||||||
请上传
|
请上传
|
||||||
<template v-if="fileSize"> 大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b> </template>
|
<template v-if="fileSize">
|
||||||
<template v-if="fileType"> 格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b> </template>
|
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
|
||||||
|
</template>
|
||||||
|
<template v-if="fileType">
|
||||||
|
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
|
||||||
|
</template>
|
||||||
的文件
|
的文件
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-dialog
|
<el-dialog
|
||||||
:visible.sync="dialogVisible"
|
v-model="dialogVisible"
|
||||||
title="预览"
|
title="预览"
|
||||||
width="800"
|
width="800px"
|
||||||
append-to-body
|
append-to-body
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
@ -42,185 +44,154 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { getToken } from "@/utils/auth";
|
import { getToken } from "@/utils/auth";
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
modelValue: [String, Object, Array],
|
||||||
value: [String, Object, Array],
|
// 图片数量限制
|
||||||
// 图片数量限制
|
limit: {
|
||||||
limit: {
|
type: Number,
|
||||||
type: Number,
|
default: 5,
|
||||||
default: 5,
|
|
||||||
},
|
|
||||||
// 大小限制(MB)
|
|
||||||
fileSize: {
|
|
||||||
type: Number,
|
|
||||||
default: 5,
|
|
||||||
},
|
|
||||||
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
|
||||||
fileType: {
|
|
||||||
type: Array,
|
|
||||||
default: () => ["png", "jpg", "jpeg"],
|
|
||||||
},
|
|
||||||
// 是否显示提示
|
|
||||||
isShowTip: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
// 大小限制(MB)
|
||||||
return {
|
fileSize: {
|
||||||
number: 0,
|
type: Number,
|
||||||
uploadList: [],
|
default: 5,
|
||||||
dialogImageUrl: "",
|
|
||||||
dialogVisible: false,
|
|
||||||
hideUpload: false,
|
|
||||||
baseUrl: process.env.VUE_APP_BASE_API,
|
|
||||||
uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
|
|
||||||
headers: {
|
|
||||||
Authorization: "Bearer " + getToken(),
|
|
||||||
},
|
|
||||||
fileList: []
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
watch: {
|
// 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||||
value: {
|
fileType: {
|
||||||
handler(val) {
|
type: Array,
|
||||||
if (val) {
|
default: () => ["png", "jpg", "jpeg"],
|
||||||
// 首先将值转为数组
|
|
||||||
const list = Array.isArray(val) ? val : this.value.split(',');
|
|
||||||
// 然后将数组转为对象数组
|
|
||||||
this.fileList = list.map(item => {
|
|
||||||
if (typeof item === "string") {
|
|
||||||
if (item.indexOf(this.baseUrl) === -1) {
|
|
||||||
item = { name: this.baseUrl + item, url: this.baseUrl + item };
|
|
||||||
} else {
|
|
||||||
item = { name: item, url: item };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.fileList = [];
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
// 是否显示提示
|
||||||
// 是否显示提示
|
isShowTip: {
|
||||||
showTip() {
|
type: Boolean,
|
||||||
return this.isShowTip && (this.fileType || this.fileSize);
|
default: true
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
});
|
||||||
// 上传前loading加载
|
|
||||||
handleBeforeUpload(file) {
|
|
||||||
let isImg = false;
|
|
||||||
if (this.fileType.length) {
|
|
||||||
let fileExtension = "";
|
|
||||||
if (file.name.lastIndexOf(".") > -1) {
|
|
||||||
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
|
||||||
}
|
|
||||||
isImg = this.fileType.some(type => {
|
|
||||||
if (file.type.indexOf(type) > -1) return true;
|
|
||||||
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
isImg = file.type.indexOf("image") > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isImg) {
|
const { proxy } = getCurrentInstance();
|
||||||
this.$modal.msgError(`文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`);
|
const emit = defineEmits();
|
||||||
return false;
|
const dialogImageUrl = ref("");
|
||||||
}
|
const dialogVisible = ref(false);
|
||||||
if (this.fileSize) {
|
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||||
const isLt = file.size / 1024 / 1024 < this.fileSize;
|
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
||||||
if (!isLt) {
|
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||||
this.$modal.msgError(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
|
const fileList = ref([]);
|
||||||
return false;
|
const showTip = computed(
|
||||||
|
() => props.isShowTip && (props.fileType || props.fileSize)
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(() => props.modelValue, val => {
|
||||||
|
if (val) {
|
||||||
|
// 首先将值转为数组
|
||||||
|
const list = Array.isArray(val) ? val : props.modelValue.split(",");
|
||||||
|
// 然后将数组转为对象数组
|
||||||
|
fileList.value = list.map(item => {
|
||||||
|
if (typeof item === "string") {
|
||||||
|
if (item.indexOf(baseUrl) === -1) {
|
||||||
|
item = { name: baseUrl + item, url: baseUrl + item };
|
||||||
|
} else {
|
||||||
|
item = { name: item, url: item };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$modal.loading("正在上传图片,请稍候...");
|
return item;
|
||||||
this.number++;
|
});
|
||||||
},
|
} else {
|
||||||
// 文件个数超出
|
fileList.value = [];
|
||||||
handleExceed() {
|
return [];
|
||||||
this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
|
}
|
||||||
},
|
});
|
||||||
// 上传成功回调
|
|
||||||
handleUploadSuccess(res, file) {
|
// 删除图片
|
||||||
if (res.code === 200) {
|
function handleRemove(file, files) {
|
||||||
this.uploadList.push({ name: res.fileName, url: res.fileName });
|
const findex = fileList.value.map(f => f.name).indexOf(file.name);
|
||||||
this.uploadedSuccessfully();
|
if (findex > -1) {
|
||||||
} else {
|
fileList.value.splice(findex, 1);
|
||||||
this.number--;
|
emit("update:modelValue", listToString(fileList.value));
|
||||||
this.$modal.closeLoading();
|
}
|
||||||
this.$modal.msgError(res.msg);
|
}
|
||||||
this.$refs.imageUpload.handleRemove(file);
|
|
||||||
this.uploadedSuccessfully();
|
// 上传成功回调
|
||||||
}
|
function handleUploadSuccess(res) {
|
||||||
},
|
fileList.value.push({ name: res.fileName, url: res.fileName });
|
||||||
// 删除图片
|
emit("update:modelValue", listToString(fileList.value));
|
||||||
handleDelete(file) {
|
proxy.$modal.closeLoading();
|
||||||
const findex = this.fileList.map(f => f.name).indexOf(file.name);
|
}
|
||||||
if(findex > -1) {
|
|
||||||
this.fileList.splice(findex, 1);
|
// 上传前loading加载
|
||||||
this.$emit("input", this.listToString(this.fileList));
|
function handleBeforeUpload(file) {
|
||||||
}
|
let isImg = false;
|
||||||
},
|
if (props.fileType.length) {
|
||||||
// 上传失败
|
let fileExtension = "";
|
||||||
handleUploadError() {
|
if (file.name.lastIndexOf(".") > -1) {
|
||||||
this.$modal.msgError("上传图片失败,请重试");
|
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
|
||||||
this.$modal.closeLoading();
|
}
|
||||||
},
|
isImg = props.fileType.some(type => {
|
||||||
// 上传结束处理
|
if (file.type.indexOf(type) > -1) return true;
|
||||||
uploadedSuccessfully() {
|
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
|
||||||
if (this.number > 0 && this.uploadList.length === this.number) {
|
return false;
|
||||||
this.fileList = this.fileList.concat(this.uploadList);
|
});
|
||||||
this.uploadList = [];
|
} else {
|
||||||
this.number = 0;
|
isImg = file.type.indexOf("image") > -1;
|
||||||
this.$emit("input", this.listToString(this.fileList));
|
}
|
||||||
this.$modal.closeLoading();
|
if (!isImg) {
|
||||||
}
|
proxy.$modal.msgError(
|
||||||
},
|
`文件格式不正确, 请上传${props.fileType.join("/")}图片格式文件!`
|
||||||
// 预览
|
);
|
||||||
handlePictureCardPreview(file) {
|
return false;
|
||||||
this.dialogImageUrl = file.url;
|
}
|
||||||
this.dialogVisible = true;
|
if (props.fileSize) {
|
||||||
},
|
const isLt = file.size / 1024 / 1024 < props.fileSize;
|
||||||
// 对象转成指定字符串分隔
|
if (!isLt) {
|
||||||
listToString(list, separator) {
|
proxy.$modal.msgError(`上传头像图片大小不能超过 ${props.fileSize} MB!`);
|
||||||
let strs = "";
|
return false;
|
||||||
separator = separator || ",";
|
|
||||||
for (let i in list) {
|
|
||||||
if (list[i].url) {
|
|
||||||
strs += list[i].url.replace(this.baseUrl, "") + separator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strs != '' ? strs.substr(0, strs.length - 1) : '';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
proxy.$modal.loading("上传中");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件个数超出
|
||||||
|
function handleExceed() {
|
||||||
|
proxy.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传失败
|
||||||
|
function handleUploadError() {
|
||||||
|
proxy.$modal.msgError("上传失败");
|
||||||
|
proxy.$modal.closeLoading();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 预览
|
||||||
|
function handlePictureCardPreview(file) {
|
||||||
|
dialogImageUrl.value = file.url;
|
||||||
|
dialogVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对象转成指定字符串分隔
|
||||||
|
function listToString(list, separator) {
|
||||||
|
let strs = "";
|
||||||
|
separator = separator || ",";
|
||||||
|
for (let i in list) {
|
||||||
|
strs += list[i].url.replace(baseUrl, "") + separator;
|
||||||
|
}
|
||||||
|
return strs != "" ? strs.substr(0, strs.length - 1) : "";
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
// .el-upload--picture-card 控制加号部分
|
// .el-upload--picture-card 控制加号部分
|
||||||
::v-deep.hide .el-upload--picture-card {
|
:deep(.hide .el-upload--picture-card) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
// 去掉动画效果
|
// 去掉动画效果
|
||||||
::v-deep .el-list-enter-active,
|
:deep(.el-list-enter-active),
|
||||||
::v-deep .el-list-leave-active {
|
:deep(.el-list-leave-active) {
|
||||||
transition: all 0s;
|
transition: all 0s;
|
||||||
}
|
}
|
||||||
|
:deep(.el-list-enter, .el-list-leave-active) {
|
||||||
::v-deep .el-list-enter, .el-list-leave-active {
|
opacity: 0;
|
||||||
opacity: 0;
|
transform: translateY(0);
|
||||||
transform: translateY(0);
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -1,106 +1,94 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{'hidden':hidden}" class="pagination-container">
|
<div :class="{ 'hidden': hidden }" class="pagination-container">
|
||||||
<el-pagination
|
<el-pagination
|
||||||
:background="background"
|
:background="background"
|
||||||
:current-page.sync="currentPage"
|
v-model:current-page="currentPage"
|
||||||
:page-size.sync="pageSize"
|
v-model:page-size="pageSize"
|
||||||
:layout="layout"
|
:layout="layout"
|
||||||
:page-sizes="pageSizes"
|
:page-sizes="pageSizes"
|
||||||
:pager-count="pagerCount"
|
:pager-count="pagerCount"
|
||||||
:total="total"
|
:total="total"
|
||||||
v-bind="$attrs"
|
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@current-change="handleCurrentChange"
|
@current-change="handleCurrentChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { scrollTo } from '@/utils/scroll-to'
|
import { scrollTo } from '@/utils/scroll-to'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'Pagination',
|
total: {
|
||||||
props: {
|
required: true,
|
||||||
total: {
|
type: Number
|
||||||
required: true,
|
},
|
||||||
type: Number
|
page: {
|
||||||
},
|
type: Number,
|
||||||
page: {
|
default: 1
|
||||||
type: Number,
|
},
|
||||||
default: 1
|
limit: {
|
||||||
},
|
type: Number,
|
||||||
limit: {
|
default: 20
|
||||||
type: Number,
|
},
|
||||||
default: 20
|
pageSizes: {
|
||||||
},
|
type: Array,
|
||||||
pageSizes: {
|
default() {
|
||||||
type: Array,
|
return [10, 20, 30, 50]
|
||||||
default() {
|
|
||||||
return [10, 20, 30, 50]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 移动端页码按钮的数量端默认值5
|
|
||||||
pagerCount: {
|
|
||||||
type: Number,
|
|
||||||
default: document.body.clientWidth < 992 ? 5 : 7
|
|
||||||
},
|
|
||||||
layout: {
|
|
||||||
type: String,
|
|
||||||
default: 'total, sizes, prev, pager, next, jumper'
|
|
||||||
},
|
|
||||||
background: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
autoScroll: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
hidden: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
// 移动端页码按钮的数量端默认值5
|
||||||
return {
|
pagerCount: {
|
||||||
};
|
type: Number,
|
||||||
|
default: document.body.clientWidth < 992 ? 5 : 7
|
||||||
},
|
},
|
||||||
computed: {
|
layout: {
|
||||||
currentPage: {
|
type: String,
|
||||||
get() {
|
default: 'total, sizes, prev, pager, next, jumper'
|
||||||
return this.page
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
this.$emit('update:page', val)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
pageSize: {
|
|
||||||
get() {
|
|
||||||
return this.limit
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
this.$emit('update:limit', val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
background: {
|
||||||
handleSizeChange(val) {
|
type: Boolean,
|
||||||
if (this.currentPage * val > this.total) {
|
default: true
|
||||||
this.currentPage = 1
|
},
|
||||||
}
|
autoScroll: {
|
||||||
this.$emit('pagination', { page: this.currentPage, limit: val })
|
type: Boolean,
|
||||||
if (this.autoScroll) {
|
default: true
|
||||||
scrollTo(0, 800)
|
},
|
||||||
}
|
hidden: {
|
||||||
},
|
type: Boolean,
|
||||||
handleCurrentChange(val) {
|
default: false
|
||||||
this.$emit('pagination', { page: val, limit: this.pageSize })
|
}
|
||||||
if (this.autoScroll) {
|
})
|
||||||
scrollTo(0, 800)
|
|
||||||
}
|
const emit = defineEmits();
|
||||||
}
|
const currentPage = computed({
|
||||||
|
get() {
|
||||||
|
return props.page
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
emit('update:page', val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const pageSize = computed({
|
||||||
|
get() {
|
||||||
|
return props.limit
|
||||||
|
},
|
||||||
|
set(val){
|
||||||
|
emit('update:limit', val)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
function handleSizeChange(val) {
|
||||||
|
emit('pagination', { page: currentPage.value, limit: val })
|
||||||
|
if (props.autoScroll) {
|
||||||
|
scrollTo(0, 800)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function handleCurrentChange(val) {
|
||||||
|
emit('pagination', { page: val, limit: pageSize.value })
|
||||||
|
if (props.autoScroll) {
|
||||||
|
scrollTo(0, 800)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="top-right-btn" :style="style">
|
<div class="top-right-btn">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
|
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top">
|
||||||
<el-button size="mini" circle icon="el-icon-search" @click="toggleSearch()" />
|
<el-button circle icon="Search" @click="toggleSearch()" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
|
<el-tooltip class="item" effect="dark" content="刷新" placement="top">
|
||||||
<el-button size="mini" circle icon="el-icon-refresh" @click="refresh()" />
|
<el-button circle icon="Refresh" @click="refresh()" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
|
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
|
||||||
<el-button size="mini" circle icon="el-icon-menu" @click="showColumn()" />
|
<el-button circle icon="Menu" @click="showColumn()" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-dialog :title="title" :visible.sync="open" append-to-body>
|
<el-dialog :title="title" v-model="open" append-to-body>
|
||||||
<el-transfer
|
<el-transfer
|
||||||
:titles="['显示', '隐藏']"
|
:titles="['显示', '隐藏']"
|
||||||
v-model="value"
|
v-model="value"
|
||||||
@ -21,84 +21,69 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
|
||||||
export default {
|
<script setup>
|
||||||
name: "RightToolbar",
|
const props = defineProps({
|
||||||
data() {
|
showSearch: {
|
||||||
return {
|
type: Boolean,
|
||||||
// 显隐数据
|
default: true,
|
||||||
value: [],
|
|
||||||
// 弹出层标题
|
|
||||||
title: "显示/隐藏",
|
|
||||||
// 是否显示弹出层
|
|
||||||
open: false,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
props: {
|
columns: {
|
||||||
showSearch: {
|
type: Array,
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
columns: {
|
|
||||||
type: Array,
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
gutter: {
|
|
||||||
type: Number,
|
|
||||||
default: 10,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
})
|
||||||
style() {
|
|
||||||
const ret = {};
|
const emits = defineEmits(['update:showSearch', 'queryTable']);
|
||||||
if (this.gutter) {
|
|
||||||
ret.marginRight = `${this.gutter / 2}px`;
|
// 显隐数据
|
||||||
}
|
const value = ref([]);
|
||||||
return ret;
|
// 弹出层标题
|
||||||
}
|
const title = ref("显示/隐藏");
|
||||||
},
|
// 是否显示弹出层
|
||||||
created() {
|
const open = ref(false);
|
||||||
// 显隐列初始默认隐藏列
|
|
||||||
for (let item in this.columns) {
|
// 搜索
|
||||||
if (this.columns[item].visible === false) {
|
function toggleSearch() {
|
||||||
this.value.push(parseInt(item));
|
emits("update:showSearch", !props.showSearch);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
// 刷新
|
||||||
methods: {
|
function refresh() {
|
||||||
// 搜索
|
emits("queryTable");
|
||||||
toggleSearch() {
|
}
|
||||||
this.$emit("update:showSearch", !this.showSearch);
|
|
||||||
},
|
// 右侧列表元素变化
|
||||||
// 刷新
|
function dataChange(data) {
|
||||||
refresh() {
|
for (let item in props.columns) {
|
||||||
this.$emit("queryTable");
|
const key = props.columns[item].key;
|
||||||
},
|
props.columns[item].visible = !data.includes(key);
|
||||||
// 右侧列表元素变化
|
}
|
||||||
dataChange(data) {
|
}
|
||||||
for (let item in this.columns) {
|
|
||||||
const key = this.columns[item].key;
|
// 打开显隐列dialog
|
||||||
this.columns[item].visible = !data.includes(key);
|
function showColumn() {
|
||||||
}
|
open.value = true;
|
||||||
},
|
}
|
||||||
// 打开显隐列dialog
|
|
||||||
showColumn() {
|
// 显隐列初始默认隐藏列
|
||||||
this.open = true;
|
for (let item in props.columns) {
|
||||||
},
|
if (props.columns[item].visible === false) {
|
||||||
},
|
value.value.push(parseInt(item));
|
||||||
};
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped>
|
|
||||||
::v-deep .el-transfer__button {
|
<style lang='scss' scoped>
|
||||||
|
:deep(.el-transfer__button) {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 12px;
|
|
||||||
display: block;
|
display: block;
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
}
|
}
|
||||||
::v-deep .el-transfer__button:first-child {
|
:deep(.el-transfer__button:first-child) {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.my-el-transfer {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -4,18 +4,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
const url = ref('http://doc.ruoyi.vip/ruoyi-vue');
|
||||||
name: 'RuoYiDoc',
|
|
||||||
data() {
|
function goto() {
|
||||||
return {
|
window.open(url.value)
|
||||||
url: 'http://doc.ruoyi.vip/ruoyi-vue'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
goto() {
|
|
||||||
window.open(this.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
@ -4,18 +4,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
const url = ref('https://gitee.com/y_project/RuoYi-Vue');
|
||||||
name: 'RuoYiGit',
|
|
||||||
data() {
|
function goto() {
|
||||||
return {
|
window.open(url.value)
|
||||||
url: 'https://gitee.com/y_project/RuoYi-Vue'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
goto() {
|
|
||||||
window.open(this.url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
@ -1,55 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" @click="click" />
|
<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'" @click="toggle" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import screenfull from 'screenfull'
|
import { useFullscreen } from '@vueuse/core'
|
||||||
|
|
||||||
export default {
|
const { isFullscreen, enter, exit, toggle } = useFullscreen();
|
||||||
name: 'Screenfull',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
isFullscreen: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.init()
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.destroy()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
click() {
|
|
||||||
if (!screenfull.isEnabled) {
|
|
||||||
this.$message({ message: '你的浏览器不支持全屏', type: 'warning' })
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
screenfull.toggle()
|
|
||||||
},
|
|
||||||
change() {
|
|
||||||
this.isFullscreen = screenfull.isFullscreen
|
|
||||||
},
|
|
||||||
init() {
|
|
||||||
if (screenfull.isEnabled) {
|
|
||||||
screenfull.on('change', this.change)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
destroy() {
|
|
||||||
if (screenfull.isEnabled) {
|
|
||||||
screenfull.off('change', this.change)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style lang='scss' scoped>
|
||||||
.screenfull-svg {
|
.screenfull-svg {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
fill: #5a5e66;;
|
fill: #5a5e66;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
vertical-align: 10px;
|
vertical-align: 10px;
|
||||||
|
@ -1,56 +1,57 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dropdown trigger="click" @command="handleSetSize">
|
<div>
|
||||||
<div>
|
<el-dropdown trigger="click" @command="handleSetSize">
|
||||||
<svg-icon class-name="size-icon" icon-class="size" />
|
<div class="size-icon--style">
|
||||||
</div>
|
<svg-icon class-name="size-icon" icon-class="size" />
|
||||||
<el-dropdown-menu slot="dropdown">
|
</div>
|
||||||
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size===item.value" :command="item.value">
|
<template #dropdown>
|
||||||
{{ item.label }}
|
<el-dropdown-menu>
|
||||||
</el-dropdown-item>
|
<el-dropdown-item v-for="item of sizeOptions" :key="item.value" :disabled="size === item.value" :command="item.value">
|
||||||
</el-dropdown-menu>
|
{{ item.label }}
|
||||||
</el-dropdown>
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
export default {
|
import { ElMessage } from 'element-plus'
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
sizeOptions: [
|
|
||||||
{ label: 'Default', value: 'default' },
|
|
||||||
{ label: 'Medium', value: 'medium' },
|
|
||||||
{ label: 'Small', value: 'small' },
|
|
||||||
{ label: 'Mini', value: 'mini' }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
size() {
|
|
||||||
return this.$store.getters.size
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleSetSize(size) {
|
|
||||||
this.$ELEMENT.size = size
|
|
||||||
this.$store.dispatch('app/setSize', size)
|
|
||||||
this.refreshView()
|
|
||||||
this.$message({
|
|
||||||
message: 'Switch Size Success',
|
|
||||||
type: 'success'
|
|
||||||
})
|
|
||||||
},
|
|
||||||
refreshView() {
|
|
||||||
// In order to make the cached page re-rendered
|
|
||||||
this.$store.dispatch('tagsView/delAllCachedViews', this.$route)
|
|
||||||
|
|
||||||
const { fullPath } = this.$route
|
const store = useStore();
|
||||||
|
const size = computed(() => store.getters.size);
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const {proxy} = getCurrentInstance();
|
||||||
|
const sizeOptions = ref([
|
||||||
|
{ label: '较大', value: 'large' },
|
||||||
|
{ label: '默认', value: 'default' },
|
||||||
|
{ label: '稍小', value: 'small' },
|
||||||
|
])
|
||||||
|
|
||||||
this.$nextTick(() => {
|
function refreshView() {
|
||||||
this.$router.replace({
|
// In order to make the cached page re-rendered
|
||||||
path: '/redirect' + fullPath
|
store.dispatch('tagsView/delAllCachedViews', route)
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const { fullPath } = route
|
||||||
|
|
||||||
|
nextTick(() => {
|
||||||
|
router.replace({
|
||||||
|
path: '/redirect' + fullPath
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
function handleSetSize(size) {
|
||||||
|
proxy.$modal.loading("正在设置布局大小,请稍候...");
|
||||||
|
store.dispatch('app/setSize', size)
|
||||||
|
setTimeout("window.location.reload()", 1000)
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
.size-icon--style {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 50px;
|
||||||
|
padding-right: 7px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" v-on="$listeners" />
|
<svg :class="svgClass" aria-hidden="true">
|
||||||
<svg v-else :class="svgClass" aria-hidden="true" v-on="$listeners">
|
<use :xlink:href="iconName" :fill="color" />
|
||||||
<use :xlink:href="iconName" />
|
|
||||||
</svg>
|
</svg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { isExternal } from '@/utils/validate'
|
export default defineComponent({
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'SvgIcon',
|
|
||||||
props: {
|
props: {
|
||||||
iconClass: {
|
iconClass: {
|
||||||
type: String,
|
type: String,
|
||||||
@ -18,44 +14,40 @@ export default {
|
|||||||
className: {
|
className: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
}
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
setup(props) {
|
||||||
isExternal() {
|
return {
|
||||||
return isExternal(this.iconClass)
|
iconName: computed(() => `#icon-${props.iconClass}`),
|
||||||
},
|
svgClass: computed(() => {
|
||||||
iconName() {
|
if (props.className) {
|
||||||
return `#icon-${this.iconClass}`
|
return `svg-icon ${props.className}`
|
||||||
},
|
}
|
||||||
svgClass() {
|
|
||||||
if (this.className) {
|
|
||||||
return 'svg-icon ' + this.className
|
|
||||||
} else {
|
|
||||||
return 'svg-icon'
|
return 'svg-icon'
|
||||||
}
|
})
|
||||||
},
|
|
||||||
styleExternalIcon() {
|
|
||||||
return {
|
|
||||||
mask: `url(${this.iconClass}) no-repeat 50% 50%`,
|
|
||||||
'-webkit-mask': `url(${this.iconClass}) no-repeat 50% 50%`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scope lang="scss">
|
||||||
|
.sub-el-icon,
|
||||||
|
.nav-icon {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-right: 12px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.svg-icon {
|
.svg-icon {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
vertical-align: -0.15em;
|
position: relative;
|
||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
overflow: hidden;
|
vertical-align: -2px;
|
||||||
}
|
|
||||||
|
|
||||||
.svg-external-icon {
|
|
||||||
background-color: currentColor;
|
|
||||||
mask-size: cover!important;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
10
ruoyi-ui/src/components/SvgIcon/svgicon.js
Normal file
10
ruoyi-ui/src/components/SvgIcon/svgicon.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import * as components from '@element-plus/icons-vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install: (app) => {
|
||||||
|
for (const key in components) {
|
||||||
|
const componentConfig = components[key];
|
||||||
|
app.component(componentConfig.name, componentConfig);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
@ -6,162 +6,157 @@
|
|||||||
>
|
>
|
||||||
<template v-for="(item, index) in topMenus">
|
<template v-for="(item, index) in topMenus">
|
||||||
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
|
<el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
|
||||||
><svg-icon :icon-class="item.meta.icon" />
|
><svg-icon :icon-class="item.meta.icon" />
|
||||||
{{ item.meta.title }}</el-menu-item
|
{{ item.meta.title }}</el-menu-item
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 顶部菜单超出数量折叠 -->
|
<!-- 顶部菜单超出数量折叠 -->
|
||||||
<el-submenu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
|
<el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
|
||||||
<template slot="title">更多菜单</template>
|
<template #title>更多菜单</template>
|
||||||
<template v-for="(item, index) in topMenus">
|
<template v-for="(item, index) in topMenus">
|
||||||
<el-menu-item
|
<el-menu-item
|
||||||
:index="item.path"
|
:index="item.path"
|
||||||
:key="index"
|
:key="index"
|
||||||
v-if="index >= visibleNumber"
|
v-if="index >= visibleNumber"
|
||||||
><svg-icon :icon-class="item.meta.icon" />
|
><svg-icon :icon-class="item.meta.icon" />
|
||||||
{{ item.meta.title }}</el-menu-item
|
{{ item.meta.title }}</el-menu-item
|
||||||
>
|
>
|
||||||
</template>
|
</template>
|
||||||
</el-submenu>
|
</el-sub-menu>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { constantRoutes } from "@/router";
|
import { constantRoutes } from "@/router"
|
||||||
|
import { isHttp } from '@/utils/validate'
|
||||||
|
|
||||||
// 隐藏侧边栏路由
|
// 顶部栏初始数
|
||||||
const hideList = ['/index', '/user/profile'];
|
const visibleNumber = ref(null);
|
||||||
|
// 是否为首次加载
|
||||||
|
const isFrist = ref(null);
|
||||||
|
// 当前激活菜单的 index
|
||||||
|
const currentIndex = ref(null);
|
||||||
|
|
||||||
export default {
|
const store = useStore();
|
||||||
data() {
|
const route = useRoute();
|
||||||
return {
|
|
||||||
// 顶部栏初始数
|
// 主题颜色
|
||||||
visibleNumber: 5,
|
const theme = computed(() => store.state.settings.theme);
|
||||||
// 当前激活菜单的 index
|
// 所有的路由信息
|
||||||
currentIndex: undefined
|
const routers = computed(() => store.state.permission.topbarRouters);
|
||||||
};
|
|
||||||
},
|
// 顶部显示菜单
|
||||||
computed: {
|
const topMenus = computed(() => {
|
||||||
theme() {
|
let topMenus = [];
|
||||||
return this.$store.state.settings.theme;
|
routers.value.map((menu) => {
|
||||||
},
|
if (menu.hidden !== true) {
|
||||||
// 顶部显示菜单
|
// 兼容顶部栏一级菜单内部跳转
|
||||||
topMenus() {
|
if (menu.path === "/") {
|
||||||
let topMenus = [];
|
topMenus.push(menu.children[0]);
|
||||||
this.routers.map((menu) => {
|
|
||||||
if (menu.hidden !== true) {
|
|
||||||
// 兼容顶部栏一级菜单内部跳转
|
|
||||||
if (menu.path === "/") {
|
|
||||||
topMenus.push(menu.children[0]);
|
|
||||||
} else {
|
|
||||||
topMenus.push(menu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return topMenus;
|
|
||||||
},
|
|
||||||
// 所有的路由信息
|
|
||||||
routers() {
|
|
||||||
return this.$store.state.permission.topbarRouters;
|
|
||||||
},
|
|
||||||
// 设置子路由
|
|
||||||
childrenMenus() {
|
|
||||||
var childrenMenus = [];
|
|
||||||
this.routers.map((router) => {
|
|
||||||
for (var item in router.children) {
|
|
||||||
if (router.children[item].parentPath === undefined) {
|
|
||||||
if(router.path === "/") {
|
|
||||||
router.children[item].path = "/" + router.children[item].path;
|
|
||||||
} else {
|
|
||||||
if(!this.ishttp(router.children[item].path)) {
|
|
||||||
router.children[item].path = router.path + "/" + router.children[item].path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
router.children[item].parentPath = router.path;
|
|
||||||
}
|
|
||||||
childrenMenus.push(router.children[item]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return constantRoutes.concat(childrenMenus);
|
|
||||||
},
|
|
||||||
// 默认激活的菜单
|
|
||||||
activeMenu() {
|
|
||||||
const path = this.$route.path;
|
|
||||||
let activePath = path;
|
|
||||||
if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
|
|
||||||
const tmpPath = path.substring(1, path.length);
|
|
||||||
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
|
|
||||||
if (!this.$route.meta.link) {
|
|
||||||
this.$store.dispatch('app/toggleSideBarHide', false);
|
|
||||||
}
|
|
||||||
} else if(!this.$route.children) {
|
|
||||||
activePath = path;
|
|
||||||
this.$store.dispatch('app/toggleSideBarHide', true);
|
|
||||||
}
|
|
||||||
this.activeRoutes(activePath);
|
|
||||||
return activePath;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
beforeMount() {
|
|
||||||
window.addEventListener('resize', this.setVisibleNumber)
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
window.removeEventListener('resize', this.setVisibleNumber)
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.setVisibleNumber();
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 根据宽度计算设置显示栏数
|
|
||||||
setVisibleNumber() {
|
|
||||||
const width = document.body.getBoundingClientRect().width / 3;
|
|
||||||
this.visibleNumber = parseInt(width / 85);
|
|
||||||
},
|
|
||||||
// 菜单选择事件
|
|
||||||
handleSelect(key, keyPath) {
|
|
||||||
this.currentIndex = key;
|
|
||||||
const route = this.routers.find(item => item.path === key);
|
|
||||||
if (this.ishttp(key)) {
|
|
||||||
// http(s):// 路径新窗口打开
|
|
||||||
window.open(key, "_blank");
|
|
||||||
} else if (!route || !route.children) {
|
|
||||||
// 没有子路由路径内部打开
|
|
||||||
const routeMenu = this.childrenMenus.find(item => item.path === key);
|
|
||||||
if (routeMenu && routeMenu.query) {
|
|
||||||
let query = JSON.parse(routeMenu.query);
|
|
||||||
this.$router.push({ path: key, query: query });
|
|
||||||
} else {
|
|
||||||
this.$router.push({ path: key });
|
|
||||||
}
|
|
||||||
this.$store.dispatch('app/toggleSideBarHide', true);
|
|
||||||
} else {
|
} else {
|
||||||
// 显示左侧联动菜单
|
topMenus.push(menu);
|
||||||
this.activeRoutes(key);
|
|
||||||
this.$store.dispatch('app/toggleSideBarHide', false);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
// 当前激活的路由
|
|
||||||
activeRoutes(key) {
|
|
||||||
var routes = [];
|
|
||||||
if (this.childrenMenus && this.childrenMenus.length > 0) {
|
|
||||||
this.childrenMenus.map((item) => {
|
|
||||||
if (key == item.parentPath || (key == "index" && "" == item.path)) {
|
|
||||||
routes.push(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if(routes.length > 0) {
|
|
||||||
this.$store.commit("SET_SIDEBAR_ROUTERS", routes);
|
|
||||||
} else {
|
|
||||||
this.$store.dispatch('app/toggleSideBarHide', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ishttp(url) {
|
|
||||||
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
|
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
};
|
return topMenus;
|
||||||
|
})
|
||||||
|
|
||||||
|
// 设置子路由
|
||||||
|
const childrenMenus = computed(() => {
|
||||||
|
let childrenMenus = [];
|
||||||
|
routers.value.map((router) => {
|
||||||
|
for (let item in router.children) {
|
||||||
|
if (router.children[item].parentPath === undefined) {
|
||||||
|
if(router.path === "/") {
|
||||||
|
router.children[item].path = "/redirect/" + router.children[item].path;
|
||||||
|
} else {
|
||||||
|
if(!isHttp(router.children[item].path)) {
|
||||||
|
router.children[item].path = router.path + "/" + router.children[item].path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
router.children[item].parentPath = router.path;
|
||||||
|
}
|
||||||
|
childrenMenus.push(router.children[item]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return constantRoutes.concat(childrenMenus);
|
||||||
|
})
|
||||||
|
|
||||||
|
// 默认激活的菜单
|
||||||
|
const activeMenu = computed(() => {
|
||||||
|
const path = route.path;
|
||||||
|
let activePath = defaultRouter.value;
|
||||||
|
if (path !== undefined && path.lastIndexOf("/") > 0) {
|
||||||
|
const tmpPath = path.substring(1, path.length);
|
||||||
|
activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
|
||||||
|
} else if ("/index" == path || "" == path) {
|
||||||
|
if (!isFrist.value) {
|
||||||
|
isFrist.value = true;
|
||||||
|
} else {
|
||||||
|
activePath = "index";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let routes = activeRoutes(activePath);
|
||||||
|
if (routes.length === 0) {
|
||||||
|
activePath = currentIndex.value || defaultRouter.value
|
||||||
|
activeRoutes(activePath);
|
||||||
|
}
|
||||||
|
return activePath;
|
||||||
|
})
|
||||||
|
// 默认激活的路由
|
||||||
|
const defaultRouter = computed(() => {
|
||||||
|
let router;
|
||||||
|
Object.keys(routers.value).some((key) => {
|
||||||
|
if (!routers.value[key].hidden) {
|
||||||
|
router = routers.value[key].path;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return router;
|
||||||
|
})
|
||||||
|
function setVisibleNumber() {
|
||||||
|
const width = document.body.getBoundingClientRect().width / 3;
|
||||||
|
visibleNumber.value = parseInt(width / 85);
|
||||||
|
}
|
||||||
|
function handleSelect(key, keyPath) {
|
||||||
|
currentIndex.value = key;
|
||||||
|
if (isHttp(key)) {
|
||||||
|
// http(s):// 路径新窗口打开
|
||||||
|
window.open(key, "_blank");
|
||||||
|
} else if (key.indexOf("/redirect") !== -1) {
|
||||||
|
// /redirect 路径内部打开
|
||||||
|
router.push({ path: key.replace("/redirect", "") });
|
||||||
|
} else {
|
||||||
|
// 显示左侧联动菜单
|
||||||
|
activeRoutes(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function activeRoutes(key) {
|
||||||
|
let routes = [];
|
||||||
|
if (childrenMenus.value && childrenMenus.value.length > 0) {
|
||||||
|
childrenMenus.value.map((item) => {
|
||||||
|
if (key == item.parentPath || (key == "index" && "" == item.path)) {
|
||||||
|
routes.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if(routes.length > 0) {
|
||||||
|
store.commit("SET_SIDEBAR_ROUTERS", routes);
|
||||||
|
}
|
||||||
|
return routes;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('resize', setVisibleNumber)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', setVisibleNumber)
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setVisibleNumber()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@ -174,13 +169,13 @@ export default {
|
|||||||
margin: 0 10px !important;
|
margin: 0 10px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-submenu.is-active .el-submenu__title {
|
.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
|
||||||
border-bottom: 2px solid #{'var(--theme)'} !important;
|
border-bottom: 2px solid #{'var(--theme)'} !important;
|
||||||
color: #303133;
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* submenu item */
|
/* sub-menu item */
|
||||||
.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title {
|
.topmenu-container.el-menu--horizontal > .el-sub-menu .el-submenu__title {
|
||||||
float: left;
|
float: left;
|
||||||
height: 50px !important;
|
height: 50px !important;
|
||||||
line-height: 50px !important;
|
line-height: 50px !important;
|
||||||
|
156
ruoyi-ui/src/components/TreeSelect/index.vue
Normal file
156
ruoyi-ui/src/components/TreeSelect/index.vue
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<div class="el-tree-select">
|
||||||
|
<el-select
|
||||||
|
style="width: 100%"
|
||||||
|
v-model="valueId"
|
||||||
|
ref="treeSelect"
|
||||||
|
:filterable="true"
|
||||||
|
:clearable="true"
|
||||||
|
@clear="clearHandle"
|
||||||
|
:filter-method="selectFilterData"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
>
|
||||||
|
<el-option :value="valueId" :label="valueTitle">
|
||||||
|
<el-tree
|
||||||
|
id="tree-option"
|
||||||
|
ref="selectTree"
|
||||||
|
:accordion="accordion"
|
||||||
|
:data="options"
|
||||||
|
:props="objMap"
|
||||||
|
:node-key="objMap.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>
|
||||||
|
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
/* 配置项 */
|
||||||
|
objMap: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {
|
||||||
|
return {
|
||||||
|
value: 'id', // ID字段名
|
||||||
|
label: 'label', // 显示名称
|
||||||
|
children: 'children' // 子级字段名
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/* 自动收起 */
|
||||||
|
accordion: {
|
||||||
|
type: Boolean,
|
||||||
|
default: () => {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**当前双向数据绑定的值 */
|
||||||
|
value: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
/**当前的数据 */
|
||||||
|
options: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
/**输入框内部的文字 */
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:value']);
|
||||||
|
|
||||||
|
const valueId = computed({
|
||||||
|
get: () => props.value,
|
||||||
|
set: (val) => {
|
||||||
|
emit('update:value', val)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const valueTitle = ref('');
|
||||||
|
const defaultExpandedKey = ref([]);
|
||||||
|
|
||||||
|
function initHandle() {
|
||||||
|
nextTick(() => {
|
||||||
|
const selectedValue = valueId.value;
|
||||||
|
if(selectedValue && selectedValue !== null && typeof (selectedValue) !== "undefined"){
|
||||||
|
const node = proxy.$refs.selectTree.getNode(selectedValue)
|
||||||
|
if (node) {
|
||||||
|
valueTitle.value = node.data[props.objMap.label]
|
||||||
|
proxy.$refs.selectTree.setCurrentKey(selectedValue) // 设置默认选中
|
||||||
|
defaultExpandedKey.value = [selectedValue] // 设置默认展开
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
clearHandle()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function handleNodeClick(node) {
|
||||||
|
valueTitle.value = node[props.objMap.label]
|
||||||
|
valueId.value = node[props.objMap.value];
|
||||||
|
defaultExpandedKey.value = [];
|
||||||
|
proxy.$refs.treeSelect.blur()
|
||||||
|
selectFilterData('')
|
||||||
|
}
|
||||||
|
function selectFilterData(val) {
|
||||||
|
proxy.$refs.selectTree.filter(val)
|
||||||
|
}
|
||||||
|
function filterNode(value, data) {
|
||||||
|
if (!value) return true
|
||||||
|
return data[props.objMap['label']].indexOf(value) !== -1
|
||||||
|
}
|
||||||
|
function clearHandle() {
|
||||||
|
valueTitle.value = ''
|
||||||
|
valueId.value = ''
|
||||||
|
defaultExpandedKey.value = [];
|
||||||
|
clearSelected()
|
||||||
|
}
|
||||||
|
function clearSelected() {
|
||||||
|
const allNode = document.querySelectorAll('#tree-option .el-tree-node')
|
||||||
|
allNode.forEach((element) => element.classList.remove('is-current'))
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initHandle()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(valueId, () => {
|
||||||
|
initHandle();
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang='scss' scoped>
|
||||||
|
@import "@/assets/styles/variables.module.scss";
|
||||||
|
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
|
||||||
|
padding: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-select-dropdown__item.selected {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li .el-tree .el-tree-node__content {
|
||||||
|
height: auto;
|
||||||
|
padding: 0 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tree-node__content:hover),
|
||||||
|
:deep(.el-tree-node__content:active),
|
||||||
|
:deep(.is-current > div:first-child),
|
||||||
|
:deep(.el-tree-node__content:focus) {
|
||||||
|
background-color: mix(#fff, $--color-primary, 90%);
|
||||||
|
color: $--color-primary;
|
||||||
|
}
|
||||||
|
</style>
|
@ -4,33 +4,28 @@
|
|||||||
:src="url"
|
:src="url"
|
||||||
frameborder="no"
|
frameborder="no"
|
||||||
style="width: 100%; height: 100%"
|
style="width: 100%; height: 100%"
|
||||||
scrolling="auto"
|
scrolling="auto" />
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
|
||||||
export default {
|
<script setup>
|
||||||
props: {
|
const props = defineProps({
|
||||||
src: {
|
src: {
|
||||||
type: String,
|
type: String,
|
||||||
default:"/"
|
required: true
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
height: document.documentElement.clientHeight - 94.5 + "px;",
|
|
||||||
loading: true,
|
|
||||||
url:this.src
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted: function () {
|
|
||||||
setTimeout(() => {
|
|
||||||
this.loading = false;
|
|
||||||
}, 300);
|
|
||||||
const that = this;
|
|
||||||
window.onresize = function temp() {
|
|
||||||
that.height = document.documentElement.clientHeight - 94.5 + "px;";
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
})
|
||||||
|
|
||||||
|
const height = ref(document.documentElement.clientHeight - 94.5 + "px;")
|
||||||
|
const loading = ref(true)
|
||||||
|
const url = computed(() => props.src)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 300);
|
||||||
|
window.onresize = function temp() {
|
||||||
|
height.value = document.documentElement.clientHeight - 94.5 + "px;";
|
||||||
|
};
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,23 +1,30 @@
|
|||||||
|
// import hasRole from './permission/hasRole'
|
||||||
|
// import hasPermi from './permission/hasPermi'
|
||||||
|
// import dialogDrag from './dialog/drag'
|
||||||
|
// import dialogDragWidth from './dialog/dragWidth'
|
||||||
|
// import dialogDragHeight from './dialog/dragHeight'
|
||||||
|
// import clipboard from './module/clipboard'
|
||||||
|
//
|
||||||
|
// const install = function(Vue) {
|
||||||
|
// Vue.directive('hasRole', hasRole)
|
||||||
|
// Vue.directive('hasPermi', hasPermi)
|
||||||
|
// Vue.directive('clipboard', clipboard)
|
||||||
|
// Vue.directive('dialogDrag', dialogDrag)
|
||||||
|
// Vue.directive('dialogDragWidth', dialogDragWidth)
|
||||||
|
// Vue.directive('dialogDragHeight', dialogDragHeight)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (window.Vue) {
|
||||||
|
// window['hasRole'] = hasRole
|
||||||
|
// window['hasPermi'] = hasPermi
|
||||||
|
// Vue.use(install); // eslint-disable-line
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// export default install
|
||||||
import hasRole from './permission/hasRole'
|
import hasRole from './permission/hasRole'
|
||||||
import hasPermi from './permission/hasPermi'
|
import hasPermi from './permission/hasPermi'
|
||||||
import dialogDrag from './dialog/drag'
|
|
||||||
import dialogDragWidth from './dialog/dragWidth'
|
|
||||||
import dialogDragHeight from './dialog/dragHeight'
|
|
||||||
import clipboard from './module/clipboard'
|
|
||||||
|
|
||||||
const install = function(Vue) {
|
export default function directive(app){
|
||||||
Vue.directive('hasRole', hasRole)
|
app.directive('hasRole', hasRole)
|
||||||
Vue.directive('hasPermi', hasPermi)
|
app.directive('hasPermi', hasPermi)
|
||||||
Vue.directive('clipboard', clipboard)
|
|
||||||
Vue.directive('dialogDrag', dialogDrag)
|
|
||||||
Vue.directive('dialogDragWidth', dialogDragWidth)
|
|
||||||
Vue.directive('dialogDragHeight', dialogDragHeight)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.Vue) {
|
|
||||||
window['hasRole'] = hasRole
|
|
||||||
window['hasPermi'] = hasPermi
|
|
||||||
Vue.use(install); // eslint-disable-line
|
|
||||||
}
|
|
||||||
|
|
||||||
export default install
|
|
||||||
|
@ -1,29 +1,22 @@
|
|||||||
<template>
|
<template>
|
||||||
<section class="app-main">
|
<section class="app-main">
|
||||||
<transition name="fade-transform" mode="out-in">
|
<router-view v-slot="{ Component, route }">
|
||||||
<keep-alive :include="cachedViews">
|
<transition name="fade-transform" mode="out-in">
|
||||||
<router-view v-if="!$route.meta.link" :key="key" />
|
<keep-alive :include="cachedViews">
|
||||||
</keep-alive>
|
<component :is="Component" :key="route.path"/>
|
||||||
</transition>
|
</keep-alive>
|
||||||
<iframe-toggle />
|
</transition>
|
||||||
|
</router-view>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import iframeToggle from "./IframeToggle/index"
|
let store = useStore()
|
||||||
|
const route = useRoute()
|
||||||
export default {
|
store.dispatch('tagsView/addCachedView', route)
|
||||||
name: 'AppMain',
|
const cachedViews = computed(() => {
|
||||||
components: { iframeToggle },
|
return store.state.tagsView.cachedViews
|
||||||
computed: {
|
})
|
||||||
cachedViews() {
|
|
||||||
return this.$store.state.tagsView.cachedViews
|
|
||||||
},
|
|
||||||
key() {
|
|
||||||
return this.$route.path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -55,21 +48,7 @@ export default {
|
|||||||
// fix css style bug in open el-dialog
|
// fix css style bug in open el-dialog
|
||||||
.el-popup-parent--hidden {
|
.el-popup-parent--hidden {
|
||||||
.fixed-header {
|
.fixed-header {
|
||||||
padding-right: 6px;
|
padding-right: 17px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
height: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
background-color: #f1f1f1;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background-color: #c0c0c0;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,24 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<transition-group name="fade-transform" mode="out-in">
|
<transition-group name="fade-transform" mode="out-in">
|
||||||
<inner-link
|
<inner-link
|
||||||
v-for="(item, index) in iframeViews"
|
v-for="(item, index) in tagsViewStore.iframeViews"
|
||||||
:key="item.path"
|
:key="item.path"
|
||||||
:iframeId="'iframe' + index"
|
:iframeId="'iframe' + index"
|
||||||
v-show="$route.path === item.path"
|
v-show="route.path === item.path"
|
||||||
:src="item.meta.link"
|
:src="item.meta.link"
|
||||||
></inner-link>
|
></inner-link>
|
||||||
</transition-group>
|
</transition-group>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import InnerLink from "../InnerLink/index"
|
import InnerLink from "../InnerLink/index.vue";
|
||||||
|
import useTagsViewStore from '@/store/modules/tagsView';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const tagsViewStore = useTagsViewStore()
|
||||||
|
|
||||||
export default {
|
|
||||||
components: { InnerLink },
|
|
||||||
computed: {
|
|
||||||
iframeViews() {
|
|
||||||
return this.$store.state.tagsView.iframeViews
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,47 +1,30 @@
|
|||||||
<template>
|
|
||||||
<div :style="'height:' + height" v-loading="loading" element-loading-text="正在加载页面,请稍候!">
|
|
||||||
<iframe
|
|
||||||
:id="iframeId"
|
|
||||||
style="width: 100%; height: 100%"
|
|
||||||
:src="src"
|
|
||||||
frameborder="no"
|
|
||||||
></iframe>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
props: {
|
setup() {
|
||||||
src: {
|
const route = useRoute();
|
||||||
type: String,
|
const link = route.meta.link;
|
||||||
default: "/"
|
if (link === "") {
|
||||||
},
|
return "404";
|
||||||
iframeId: {
|
|
||||||
type: String
|
|
||||||
}
|
}
|
||||||
|
let url = link;
|
||||||
|
const height = document.documentElement.clientHeight - 94.5 + "px";
|
||||||
|
const style = { height: height };
|
||||||
|
|
||||||
|
// 返回渲染函数
|
||||||
|
return () =>
|
||||||
|
h(
|
||||||
|
"div",
|
||||||
|
{
|
||||||
|
style: style,
|
||||||
|
},
|
||||||
|
h("iframe", {
|
||||||
|
src: url,
|
||||||
|
frameborder: "no",
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
scrolling: "auto",
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
height: document.documentElement.clientHeight - 94.5 + "px;"
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
var _this = this;
|
|
||||||
const iframeId = ("#" + this.iframeId).replace(/\//g, "\\/");
|
|
||||||
const iframe = document.querySelector(iframeId);
|
|
||||||
// iframe页面loading控制
|
|
||||||
if (iframe.attachEvent) {
|
|
||||||
this.loading = true;
|
|
||||||
iframe.attachEvent("onload", function () {
|
|
||||||
_this.loading = false;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.loading = true;
|
|
||||||
iframe.onload = function () {
|
|
||||||
_this.loading = false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="navbar">
|
<div class="navbar">
|
||||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
<hamburger id="hamburger-container" :is-active="getters.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
|
||||||
|
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!$store.state.settings.topNav" />
|
||||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav"/>
|
<top-nav id="topmenu-container" class="topmenu-container" v-if="$store.state.settings.topNav" />
|
||||||
<top-nav id="topmenu-container" class="topmenu-container" v-if="topNav"/>
|
|
||||||
|
|
||||||
<div class="right-menu">
|
<div class="right-menu">
|
||||||
<template v-if="device!=='mobile'">
|
<template v-if="getters.device !== 'mobile'">
|
||||||
<search id="header-search" class="right-menu-item" />
|
<header-search id="header-search" class="right-menu-item" />
|
||||||
|
|
||||||
<el-tooltip content="源码地址" effect="dark" placement="bottom">
|
<el-tooltip content="源码地址" effect="dark" placement="bottom">
|
||||||
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
|
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
|
||||||
@ -22,112 +21,99 @@
|
|||||||
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
<el-tooltip content="布局大小" effect="dark" placement="bottom">
|
||||||
<size-select id="size-select" class="right-menu-item hover-effect" />
|
<size-select id="size-select" class="right-menu-item hover-effect" />
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
<div class="avatar-container">
|
||||||
<el-dropdown class="avatar-container right-menu-item hover-effect" trigger="click">
|
<el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
|
||||||
<div class="avatar-wrapper">
|
<div class="avatar-wrapper">
|
||||||
<img :src="avatar" class="user-avatar">
|
<img :src="getters.avatar" class="user-avatar" />
|
||||||
<i class="el-icon-caret-bottom" />
|
<el-icon><caret-bottom /></el-icon>
|
||||||
</div>
|
</div>
|
||||||
<el-dropdown-menu slot="dropdown">
|
<template #dropdown>
|
||||||
<router-link to="/user/profile">
|
<el-dropdown-menu>
|
||||||
<el-dropdown-item>个人中心</el-dropdown-item>
|
<router-link to="/user/profile">
|
||||||
</router-link>
|
<el-dropdown-item>个人中心</el-dropdown-item>
|
||||||
<el-dropdown-item @click.native="setting = true">
|
</router-link>
|
||||||
<span>布局设置</span>
|
<el-dropdown-item command="setLayout">
|
||||||
</el-dropdown-item>
|
<span>布局设置</span>
|
||||||
<el-dropdown-item divided @click.native="logout">
|
</el-dropdown-item>
|
||||||
<span>退出登录</span>
|
<el-dropdown-item divided command="logout">
|
||||||
</el-dropdown-item>
|
<span>退出登录</span>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-item>
|
||||||
</el-dropdown>
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { mapGetters } from 'vuex'
|
import { ElMessageBox } from 'element-plus'
|
||||||
import Breadcrumb from '@/components/Breadcrumb'
|
import Breadcrumb from '@/components/Breadcrumb'
|
||||||
import TopNav from '@/components/TopNav'
|
import TopNav from '@/components/TopNav'
|
||||||
import Hamburger from '@/components/Hamburger'
|
import Hamburger from '@/components/Hamburger'
|
||||||
import Screenfull from '@/components/Screenfull'
|
import Screenfull from '@/components/Screenfull'
|
||||||
import SizeSelect from '@/components/SizeSelect'
|
import SizeSelect from '@/components/SizeSelect'
|
||||||
import Search from '@/components/HeaderSearch'
|
import HeaderSearch from '@/components/HeaderSearch'
|
||||||
import RuoYiGit from '@/components/RuoYi/Git'
|
import RuoYiGit from '@/components/RuoYi/Git'
|
||||||
import RuoYiDoc from '@/components/RuoYi/Doc'
|
import RuoYiDoc from '@/components/RuoYi/Doc'
|
||||||
|
|
||||||
export default {
|
const store = useStore();
|
||||||
components: {
|
const getters = computed(() => store.getters);
|
||||||
Breadcrumb,
|
|
||||||
TopNav,
|
function toggleSideBar() {
|
||||||
Hamburger,
|
store.dispatch('app/toggleSideBar')
|
||||||
Screenfull,
|
}
|
||||||
SizeSelect,
|
|
||||||
Search,
|
function handleCommand(command) {
|
||||||
RuoYiGit,
|
switch (command) {
|
||||||
RuoYiDoc
|
case "setLayout":
|
||||||
},
|
setLayout();
|
||||||
computed: {
|
break;
|
||||||
...mapGetters([
|
case "logout":
|
||||||
'sidebar',
|
logout();
|
||||||
'avatar',
|
break;
|
||||||
'device'
|
default:
|
||||||
]),
|
break;
|
||||||
setting: {
|
|
||||||
get() {
|
|
||||||
return this.$store.state.settings.showSettings
|
|
||||||
},
|
|
||||||
set(val) {
|
|
||||||
this.$store.dispatch('settings/changeSetting', {
|
|
||||||
key: 'showSettings',
|
|
||||||
value: val
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
topNav: {
|
|
||||||
get() {
|
|
||||||
return this.$store.state.settings.topNav
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggleSideBar() {
|
|
||||||
this.$store.dispatch('app/toggleSideBar')
|
|
||||||
},
|
|
||||||
async logout() {
|
|
||||||
this.$confirm('确定注销并退出系统吗?', '提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning'
|
|
||||||
}).then(() => {
|
|
||||||
this.$store.dispatch('LogOut').then(() => {
|
|
||||||
location.href = '/index';
|
|
||||||
})
|
|
||||||
}).catch(() => {});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
ElMessageBox.confirm('确定注销并退出系统吗?', '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
store.dispatch('LogOut').then(() => {
|
||||||
|
location.href = '/index';
|
||||||
|
})
|
||||||
|
}).catch(() => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
const emits = defineEmits(['setLayout'])
|
||||||
|
function setLayout() {
|
||||||
|
emits('setLayout');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang='scss' scoped>
|
||||||
.navbar {
|
.navbar {
|
||||||
height: 50px;
|
height: 50px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
box-shadow: 0 1px 4px rgba(0,21,41,.08);
|
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||||
|
|
||||||
.hamburger-container {
|
.hamburger-container {
|
||||||
line-height: 46px;
|
line-height: 46px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
float: left;
|
float: left;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background .3s;
|
transition: background 0.3s;
|
||||||
-webkit-tap-highlight-color:transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, .025)
|
background: rgba(0, 0, 0, 0.025);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +135,7 @@ export default {
|
|||||||
float: right;
|
float: right;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: 50px;
|
line-height: 50px;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
@ -164,16 +151,16 @@ export default {
|
|||||||
|
|
||||||
&.hover-effect {
|
&.hover-effect {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: background .3s;
|
transition: background 0.3s;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgba(0, 0, 0, .025)
|
background: rgba(0, 0, 0, 0.025);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.avatar-container {
|
.avatar-container {
|
||||||
margin-right: 30px;
|
margin-right: 40px;
|
||||||
|
|
||||||
.avatar-wrapper {
|
.avatar-wrapper {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
@ -186,7 +173,7 @@ export default {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-icon-caret-bottom {
|
i {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: -20px;
|
right: -20px;
|
||||||
|
@ -1,260 +1,254 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-drawer size="280px" :visible="visible" :with-header="false" :append-to-body="true" :show-close="false">
|
<el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px">
|
||||||
<div class="drawer-container">
|
<div class="setting-drawer-title">
|
||||||
<div>
|
<h3 class="drawer-title">主题风格设置</h3>
|
||||||
<div class="setting-drawer-content">
|
</div>
|
||||||
<div class="setting-drawer-title">
|
<div class="setting-drawer-block-checbox">
|
||||||
<h3 class="drawer-title">主题风格设置</h3>
|
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
|
||||||
</div>
|
<img src="@/assets/images/dark.svg" alt="dark" />
|
||||||
<div class="setting-drawer-block-checbox">
|
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
|
||||||
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-dark')">
|
<i aria-label="图标: check" class="anticon anticon-check">
|
||||||
<img src="@/assets/images/dark.svg" alt="dark">
|
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
|
||||||
<div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
|
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
|
||||||
<i aria-label="图标: check" class="anticon anticon-check">
|
</svg>
|
||||||
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class="">
|
</i>
|
||||||
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"/>
|
|
||||||
</svg>
|
|
||||||
</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')">
|
|
||||||
<img src="@/assets/images/light.svg" alt="light">
|
|
||||||
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
|
|
||||||
<i aria-label="图标: check" class="anticon anticon-check">
|
|
||||||
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class="">
|
|
||||||
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"/>
|
|
||||||
</svg>
|
|
||||||
</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="drawer-item">
|
|
||||||
<span>主题颜色</span>
|
|
||||||
<theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<el-divider/>
|
<div class="setting-drawer-block-checbox-item" @click="handleTheme('theme-light')">
|
||||||
|
<img src="@/assets/images/light.svg" alt="light" />
|
||||||
<h3 class="drawer-title">系统布局配置</h3>
|
<div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
|
||||||
|
<i aria-label="图标: check" class="anticon anticon-check">
|
||||||
<div class="drawer-item">
|
<svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
|
||||||
<span>开启 TopNav</span>
|
<path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z" />
|
||||||
<el-switch v-model="topNav" class="drawer-switch" />
|
</svg>
|
||||||
|
</i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="drawer-item">
|
|
||||||
<span>开启 Tags-Views</span>
|
|
||||||
<el-switch v-model="tagsView" class="drawer-switch" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="drawer-item">
|
|
||||||
<span>固定 Header</span>
|
|
||||||
<el-switch v-model="fixedHeader" class="drawer-switch" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="drawer-item">
|
|
||||||
<span>显示 Logo</span>
|
|
||||||
<el-switch v-model="sidebarLogo" class="drawer-switch" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="drawer-item">
|
|
||||||
<span>动态标题</span>
|
|
||||||
<el-switch v-model="dynamicTitle" class="drawer-switch" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-divider/>
|
|
||||||
|
|
||||||
<el-button size="small" type="primary" plain icon="el-icon-document-add" @click="saveSetting">保存配置</el-button>
|
|
||||||
<el-button size="small" plain icon="el-icon-refresh" @click="resetSetting">重置配置</el-button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="drawer-item">
|
||||||
|
<span>主题颜色</span>
|
||||||
|
<span class="comp-style">
|
||||||
|
<el-color-picker v-model="theme" :predefine="predefineColors" @change="themeChange"/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-divider />
|
||||||
|
|
||||||
|
<h3 class="drawer-title">系统布局配置</h3>
|
||||||
|
|
||||||
|
<div class="drawer-item">
|
||||||
|
<span>开启 TopNav</span>
|
||||||
|
<span class="comp-style">
|
||||||
|
<el-switch v-model="topNav" class="drawer-switch" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-item">
|
||||||
|
<span>开启 Tags-Views</span>
|
||||||
|
<span class="comp-style">
|
||||||
|
<el-switch v-model="tagsView" class="drawer-switch" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-item">
|
||||||
|
<span>固定 Header</span>
|
||||||
|
<span class="comp-style">
|
||||||
|
<el-switch v-model="fixedHeader" class="drawer-switch" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-item">
|
||||||
|
<span>显示 Logo</span>
|
||||||
|
<span class="comp-style">
|
||||||
|
<el-switch v-model="sidebarLogo" class="drawer-switch" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="drawer-item">
|
||||||
|
<span>动态标题</span>
|
||||||
|
<span class="comp-style">
|
||||||
|
<el-switch v-model="dynamicTitle" class="drawer-switch" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider />
|
||||||
|
|
||||||
|
<el-button type="primary" plain icon="DocumentAdd" @click="saveSetting">保存配置</el-button>
|
||||||
|
<el-button plain icon="Refresh" @click="resetSetting">重置配置</el-button>
|
||||||
</el-drawer>
|
</el-drawer>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import ThemePicker from '@/components/ThemePicker'
|
import variables from '@/assets/styles/variables.module.scss'
|
||||||
|
import originElementPlus from 'element-plus/theme-chalk/index.css'
|
||||||
|
import axios from 'axios'
|
||||||
|
import { ElLoading, ElMessage } from 'element-plus'
|
||||||
|
import { useDynamicTitle } from '@/utils/dynamicTitle'
|
||||||
|
|
||||||
export default {
|
const { proxy } = getCurrentInstance();
|
||||||
components: { ThemePicker },
|
const store = useStore();
|
||||||
data() {
|
const showSettings = ref(false);
|
||||||
return {
|
const theme = ref(store.state.settings.theme);
|
||||||
theme: this.$store.state.settings.theme,
|
const sideTheme = ref(store.state.settings.sideTheme);
|
||||||
sideTheme: this.$store.state.settings.sideTheme
|
const storeSettings = computed(() => store.state.settings);
|
||||||
};
|
const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
|
||||||
},
|
|
||||||
computed: {
|
/** 是否需要topnav */
|
||||||
visible: {
|
const topNav = computed({
|
||||||
get() {
|
get: () => storeSettings.value.topNav,
|
||||||
return this.$store.state.settings.showSettings
|
set: (val) => {
|
||||||
}
|
store.dispatch('settings/changeSetting', {
|
||||||
},
|
key: 'topNav',
|
||||||
fixedHeader: {
|
value: val
|
||||||
get() {
|
})
|
||||||
return this.$store.state.settings.fixedHeader
|
if (!val) {
|
||||||
},
|
store.commit("SET_SIDEBAR_ROUTERS", store.state.permission.defaultRoutes);
|
||||||
set(val) {
|
}
|
||||||
this.$store.dispatch('settings/changeSetting', {
|
}
|
||||||
key: 'fixedHeader',
|
})
|
||||||
value: val
|
/** 是否需要tagview */
|
||||||
})
|
const tagsView = computed({
|
||||||
}
|
get: () => storeSettings.value.tagsView,
|
||||||
},
|
set: (val) => {
|
||||||
topNav: {
|
store.dispatch('settings/changeSetting', {
|
||||||
get() {
|
key: 'tagsView',
|
||||||
return this.$store.state.settings.topNav
|
value: val
|
||||||
},
|
})
|
||||||
set(val) {
|
}
|
||||||
this.$store.dispatch('settings/changeSetting', {
|
})
|
||||||
key: 'topNav',
|
/**是否需要固定头部 */
|
||||||
value: val
|
const fixedHeader = computed({
|
||||||
})
|
get: () => storeSettings.value.fixedHeader,
|
||||||
if (!val) {
|
set: (val) => {
|
||||||
this.$store.dispatch('app/toggleSideBarHide', false);
|
store.dispatch('settings/changeSetting', {
|
||||||
this.$store.commit("SET_SIDEBAR_ROUTERS", this.$store.state.permission.defaultRoutes);
|
key: 'fixedHeader',
|
||||||
}
|
value: val
|
||||||
}
|
})
|
||||||
},
|
}
|
||||||
tagsView: {
|
})
|
||||||
get() {
|
/**是否需要侧边栏的logo */
|
||||||
return this.$store.state.settings.tagsView
|
const sidebarLogo = computed({
|
||||||
},
|
get: () => storeSettings.value.sidebarLogo,
|
||||||
set(val) {
|
set: (val) => {
|
||||||
this.$store.dispatch('settings/changeSetting', {
|
store.dispatch('settings/changeSetting', {
|
||||||
key: 'tagsView',
|
key: 'sidebarLogo',
|
||||||
value: val
|
value: val
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
sidebarLogo: {
|
/**是否需要侧边栏的动态网页的title */
|
||||||
get() {
|
const dynamicTitle = computed({
|
||||||
return this.$store.state.settings.sidebarLogo
|
get: () => storeSettings.value.dynamicTitle,
|
||||||
},
|
set: (val) => {
|
||||||
set(val) {
|
store.dispatch('settings/changeSetting', {
|
||||||
this.$store.dispatch('settings/changeSetting', {
|
key: 'dynamicTitle',
|
||||||
key: 'sidebarLogo',
|
value: val
|
||||||
value: val
|
})
|
||||||
})
|
// 动态设置网页标题
|
||||||
}
|
useDynamicTitle()
|
||||||
},
|
}
|
||||||
dynamicTitle: {
|
})
|
||||||
get() {
|
|
||||||
return this.$store.state.settings.dynamicTitle
|
function themeChange(val) {
|
||||||
},
|
store.dispatch('settings/changeSetting', {
|
||||||
set(val) {
|
key: 'theme',
|
||||||
this.$store.dispatch('settings/changeSetting', {
|
value: val
|
||||||
key: 'dynamicTitle',
|
})
|
||||||
value: val
|
theme.value = val;
|
||||||
})
|
}
|
||||||
}
|
function handleTheme(val) {
|
||||||
},
|
store.dispatch('settings/changeSetting', {
|
||||||
},
|
key: 'sideTheme',
|
||||||
methods: {
|
value: val
|
||||||
themeChange(val) {
|
})
|
||||||
this.$store.dispatch('settings/changeSetting', {
|
sideTheme.value = val;
|
||||||
key: 'theme',
|
}
|
||||||
value: val
|
function saveSetting() {
|
||||||
})
|
proxy.$modal.loading("正在保存到本地,请稍候...");
|
||||||
this.theme = val;
|
let layoutSetting = {
|
||||||
},
|
"topNav": storeSettings.value.topNav,
|
||||||
handleTheme(val) {
|
"tagsView": storeSettings.value.tagsView,
|
||||||
this.$store.dispatch('settings/changeSetting', {
|
"fixedHeader": storeSettings.value.fixedHeader,
|
||||||
key: 'sideTheme',
|
"sidebarLogo": storeSettings.value.sidebarLogo,
|
||||||
value: val
|
"dynamicTitle": storeSettings.value.dynamicTitle,
|
||||||
})
|
"sideTheme": storeSettings.value.sideTheme,
|
||||||
this.sideTheme = val;
|
"theme": storeSettings.value.theme
|
||||||
},
|
};
|
||||||
saveSetting() {
|
localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
|
||||||
this.$modal.loading("正在保存到本地,请稍候...");
|
setTimeout(proxy.$modal.closeLoading(), 1000)
|
||||||
this.$cache.local.set(
|
}
|
||||||
"layout-setting",
|
function resetSetting() {
|
||||||
`{
|
proxy.$modal.loading("正在清除设置缓存并刷新,请稍候...");
|
||||||
"topNav":${this.topNav},
|
localStorage.removeItem("layout-setting")
|
||||||
"tagsView":${this.tagsView},
|
setTimeout("window.location.reload()", 1000)
|
||||||
"fixedHeader":${this.fixedHeader},
|
}
|
||||||
"sidebarLogo":${this.sidebarLogo},
|
function openSetting() {
|
||||||
"dynamicTitle":${this.dynamicTitle},
|
showSettings.value = true;
|
||||||
"sideTheme":"${this.sideTheme}",
|
}
|
||||||
"theme":"${this.theme}"
|
|
||||||
}`
|
defineExpose({
|
||||||
);
|
openSetting,
|
||||||
setTimeout(this.$modal.closeLoading(), 1000)
|
})
|
||||||
},
|
</script>
|
||||||
resetSetting() {
|
|
||||||
this.$modal.loading("正在清除设置缓存并刷新,请稍候...");
|
<style lang='scss' scoped>
|
||||||
this.$cache.local.remove("layout-setting")
|
.setting-drawer-title {
|
||||||
setTimeout("window.location.reload()", 1000)
|
margin-bottom: 12px;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
line-height: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
.drawer-title {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setting-drawer-block-checbox {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
.setting-drawer-block-checbox-item {
|
||||||
|
position: relative;
|
||||||
|
margin-right: 16px;
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-img {
|
||||||
|
width: 48px;
|
||||||
|
height: 38px;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 1px 1px 2px #898484;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-drawer-block-checbox-selectIcon {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding-top: 15px;
|
||||||
|
padding-left: 24px;
|
||||||
|
color: #1890ff;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
.drawer-item {
|
||||||
.setting-drawer-content {
|
color: rgba(0, 0, 0, 0.65);
|
||||||
.setting-drawer-title {
|
padding: 12px 0;
|
||||||
margin-bottom: 12px;
|
font-size: 14px;
|
||||||
color: rgba(0, 0, 0, .85);
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 22px;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-drawer-block-checbox {
|
.comp-style {
|
||||||
display: flex;
|
float: right;
|
||||||
justify-content: flex-start;
|
margin: -3px 8px 0px 0px;
|
||||||
align-items: center;
|
|
||||||
margin-top: 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
.setting-drawer-block-checbox-item {
|
|
||||||
position: relative;
|
|
||||||
margin-right: 16px;
|
|
||||||
border-radius: 2px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-drawer-block-checbox-selectIcon {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding-top: 15px;
|
|
||||||
padding-left: 24px;
|
|
||||||
color: #1890ff;
|
|
||||||
font-weight: 700;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-container {
|
|
||||||
padding: 20px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
word-wrap: break-word;
|
|
||||||
|
|
||||||
.drawer-title {
|
|
||||||
margin-bottom: 12px;
|
|
||||||
color: rgba(0, 0, 0, .85);
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 22px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-item {
|
|
||||||
color: rgba(0, 0, 0, .65);
|
|
||||||
font-size: 14px;
|
|
||||||
padding: 12px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drawer-switch {
|
|
||||||
float: right
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'MenuItem',
|
|
||||||
functional: true,
|
|
||||||
props: {
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render(h, context) {
|
|
||||||
const { icon, title } = context.props
|
|
||||||
const vnodes = []
|
|
||||||
|
|
||||||
if (icon) {
|
|
||||||
vnodes.push(<svg-icon icon-class={icon}/>)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (title) {
|
|
||||||
if (title.length > 5) {
|
|
||||||
vnodes.push(<span slot='title' title={(title)}>{(title)}</span>)
|
|
||||||
} else {
|
|
||||||
vnodes.push(<span slot='title'>{(title)}</span>)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return vnodes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,43 +1,40 @@
|
|||||||
<template>
|
<template>
|
||||||
<component :is="type" v-bind="linkProps(to)">
|
<component :is="type" v-bind="linkProps()">
|
||||||
<slot />
|
<slot />
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { isExternal } from '@/utils/validate'
|
import { isExternal } from '@/utils/validate'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
props: {
|
to: {
|
||||||
to: {
|
type: [String, Object],
|
||||||
type: [String, Object],
|
required: true
|
||||||
required: true
|
}
|
||||||
}
|
})
|
||||||
},
|
|
||||||
computed: {
|
const isExt = computed(() => {
|
||||||
isExternal() {
|
return isExternal(props.to)
|
||||||
return isExternal(this.to)
|
})
|
||||||
},
|
|
||||||
type() {
|
const type = computed(() => {
|
||||||
if (this.isExternal) {
|
if (isExt.value) {
|
||||||
return 'a'
|
return 'a'
|
||||||
}
|
}
|
||||||
return 'router-link'
|
return 'router-link'
|
||||||
}
|
})
|
||||||
},
|
|
||||||
methods: {
|
function linkProps() {
|
||||||
linkProps(to) {
|
if (isExt.value) {
|
||||||
if (this.isExternal) {
|
return {
|
||||||
return {
|
href: props.to,
|
||||||
href: to,
|
target: '_blank',
|
||||||
target: '_blank',
|
rel: 'noopener'
|
||||||
rel: 'noopener'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
to: to
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
to: props.to
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,45 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="sidebar-logo-container" :class="{'collapse':collapse}" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
<div class="sidebar-logo-container" :class="{ 'collapse': collapse }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
||||||
<transition name="sidebarLogoFade">
|
<transition name="sidebarLogoFade">
|
||||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||||
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
|
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||||
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }} </h1>
|
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">{{ title }}</h1>
|
||||||
</router-link>
|
</router-link>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import logoImg from '@/assets/logo/logo.png'
|
import variables from '@/assets/styles/variables.module.scss'
|
||||||
import variables from '@/assets/styles/variables.scss'
|
import logo from '@/assets/logo/logo.png'
|
||||||
|
|
||||||
export default {
|
defineProps({
|
||||||
name: 'SidebarLogo',
|
collapse: {
|
||||||
props: {
|
type: Boolean,
|
||||||
collapse: {
|
required: true
|
||||||
type: Boolean,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
variables() {
|
|
||||||
return variables;
|
|
||||||
},
|
|
||||||
sideTheme() {
|
|
||||||
return this.$store.state.settings.sideTheme
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
title: process.env.VUE_APP_TITLE,
|
|
||||||
logo: logoImg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const title = ref('Ruoyi-Flex管理系统');
|
||||||
|
const store = useStore();
|
||||||
|
const sideTheme = computed(() => store.state.settings.sideTheme);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
@ -1,17 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="!item.hidden">
|
<div v-if="!item.hidden">
|
||||||
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
|
<template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
|
||||||
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
|
<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path, onlyOneChild.query)">
|
||||||
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
|
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
|
||||||
<item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" />
|
<svg-icon :icon-class="onlyOneChild.meta.icon || (item.meta && item.meta.icon)"/>
|
||||||
|
<template #title>{{ onlyOneChild.meta.title }}</template>
|
||||||
</el-menu-item>
|
</el-menu-item>
|
||||||
</app-link>
|
</app-link>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
|
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
|
||||||
<template slot="title">
|
<template v-if="item.meta" #title>
|
||||||
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
|
<svg-icon :icon-class="item.meta && item.meta.icon" />
|
||||||
|
<span>{{ item.meta.title }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<sidebar-item
|
<sidebar-item
|
||||||
v-for="child in item.children"
|
v-for="child in item.children"
|
||||||
:key="child.path"
|
:key="child.path"
|
||||||
@ -20,81 +23,72 @@
|
|||||||
:base-path="resolvePath(child.path)"
|
:base-path="resolvePath(child.path)"
|
||||||
class="nest-menu"
|
class="nest-menu"
|
||||||
/>
|
/>
|
||||||
</el-submenu>
|
</el-sub-menu>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import path from 'path'
|
|
||||||
import { isExternal } from '@/utils/validate'
|
import { isExternal } from '@/utils/validate'
|
||||||
import Item from './Item'
|
|
||||||
import AppLink from './Link'
|
import AppLink from './Link'
|
||||||
import FixiOSBug from './FixiOSBug'
|
import { getNormalPath } from '@/utils/ruoyi'
|
||||||
|
|
||||||
export default {
|
const props = defineProps({
|
||||||
name: 'SidebarItem',
|
// route object
|
||||||
components: { Item, AppLink },
|
item: {
|
||||||
mixins: [FixiOSBug],
|
type: Object,
|
||||||
props: {
|
required: true
|
||||||
// route object
|
|
||||||
item: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
isNest: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
basePath: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
isNest: {
|
||||||
this.onlyOneChild = null
|
type: Boolean,
|
||||||
return {}
|
default: false
|
||||||
},
|
},
|
||||||
methods: {
|
basePath: {
|
||||||
hasOneShowingChild(children = [], parent) {
|
type: String,
|
||||||
if (!children) {
|
default: ''
|
||||||
children = [];
|
|
||||||
}
|
|
||||||
const showingChildren = children.filter(item => {
|
|
||||||
if (item.hidden) {
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
// Temp set(will be used if only has one showing child)
|
|
||||||
this.onlyOneChild = item
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// When there is only one child router, the child router is displayed by default
|
|
||||||
if (showingChildren.length === 1) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show parent if there are no child router to display
|
|
||||||
if (showingChildren.length === 0) {
|
|
||||||
this.onlyOneChild = { ... parent, path: '', noShowingChildren: true }
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
resolvePath(routePath, routeQuery) {
|
|
||||||
if (isExternal(routePath)) {
|
|
||||||
return routePath
|
|
||||||
}
|
|
||||||
if (isExternal(this.basePath)) {
|
|
||||||
return this.basePath
|
|
||||||
}
|
|
||||||
if (routeQuery) {
|
|
||||||
let query = JSON.parse(routeQuery);
|
|
||||||
return { path: path.resolve(this.basePath, routePath), query: query }
|
|
||||||
}
|
|
||||||
return path.resolve(this.basePath, routePath)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const onlyOneChild = ref({});
|
||||||
|
|
||||||
|
function hasOneShowingChild(children = [], parent) {
|
||||||
|
if (!children) {
|
||||||
|
children = [];
|
||||||
|
}
|
||||||
|
const showingChildren = children.filter(item => {
|
||||||
|
if (item.hidden) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
// Temp set(will be used if only has one showing child)
|
||||||
|
onlyOneChild.value = item
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// When there is only one child router, the child router is displayed by default
|
||||||
|
if (showingChildren.length === 1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show parent if there are no child router to display
|
||||||
|
if (showingChildren.length === 0) {
|
||||||
|
onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
};
|
||||||
|
|
||||||
|
function resolvePath(routePath, routeQuery) {
|
||||||
|
if (isExternal(routePath)) {
|
||||||
|
return routePath
|
||||||
|
}
|
||||||
|
if (isExternal(props.basePath)) {
|
||||||
|
return props.basePath
|
||||||
|
}
|
||||||
|
if (routeQuery) {
|
||||||
|
let query = JSON.parse(routeQuery);
|
||||||
|
return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
|
||||||
|
}
|
||||||
|
return getNormalPath(props.basePath + '/' + routePath)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,57 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="{'has-logo':showLogo}" :style="{ backgroundColor: settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
<div :class="{ 'has-logo': showLogo }" :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
||||||
<logo v-if="showLogo" :collapse="isCollapse" />
|
<logo v-if="showLogo" :collapse="isCollapse" />
|
||||||
<el-scrollbar :class="settings.sideTheme" wrap-class="scrollbar-wrapper">
|
<el-scrollbar :class="sideTheme" wrap-class="scrollbar-wrapper">
|
||||||
<el-menu
|
<el-menu
|
||||||
:default-active="activeMenu"
|
:default-active="activeMenu"
|
||||||
:collapse="isCollapse"
|
:collapse="isCollapse"
|
||||||
:background-color="settings.sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
|
:background-color="sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground"
|
||||||
:text-color="settings.sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
|
:text-color="sideTheme === 'theme-dark' ? variables.menuColor : variables.menuLightColor"
|
||||||
:unique-opened="true"
|
:unique-opened="true"
|
||||||
:active-text-color="settings.theme"
|
:active-text-color="theme"
|
||||||
:collapse-transition="false"
|
:collapse-transition="false"
|
||||||
mode="vertical"
|
mode="vertical"
|
||||||
>
|
>
|
||||||
<sidebar-item
|
<sidebar-item
|
||||||
v-for="(route, index) in sidebarRouters"
|
v-for="(route, index) in sidebarRouters"
|
||||||
:key="route.path + index"
|
:key="route.path + index"
|
||||||
:item="route"
|
:item="route"
|
||||||
:base-path="route.path"
|
:base-path="route.path"
|
||||||
/>
|
/>
|
||||||
</el-menu>
|
</el-menu>
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { mapGetters, mapState } from "vuex";
|
import Logo from './Logo'
|
||||||
import Logo from "./Logo";
|
import SidebarItem from './SidebarItem'
|
||||||
import SidebarItem from "./SidebarItem";
|
import variables from '@/assets/styles/variables.module.scss'
|
||||||
import variables from "@/assets/styles/variables.scss";
|
|
||||||
|
const route = useRoute();
|
||||||
|
const store = useStore();
|
||||||
|
|
||||||
|
const sidebarRouters = computed(() => store.getters.sidebarRouters);
|
||||||
|
const showLogo = computed(() => store.state.settings.sidebarLogo);
|
||||||
|
const sideTheme = computed(() => store.state.settings.sideTheme);
|
||||||
|
const theme = computed(() => store.state.settings.theme);
|
||||||
|
const isCollapse = computed(() => !store.state.app.sidebar.opened);
|
||||||
|
|
||||||
|
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 path;
|
||||||
|
})
|
||||||
|
|
||||||
export default {
|
|
||||||
components: { SidebarItem, Logo },
|
|
||||||
computed: {
|
|
||||||
...mapState(["settings"]),
|
|
||||||
...mapGetters(["sidebarRouters", "sidebar"]),
|
|
||||||
activeMenu() {
|
|
||||||
const route = this.$route;
|
|
||||||
const { meta, path } = route;
|
|
||||||
// if set path, the sidebar will highlight the path you set
|
|
||||||
if (meta.activeMenu) {
|
|
||||||
return meta.activeMenu;
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
},
|
|
||||||
showLogo() {
|
|
||||||
return this.$store.state.settings.sidebarLogo;
|
|
||||||
},
|
|
||||||
variables() {
|
|
||||||
return variables;
|
|
||||||
},
|
|
||||||
isCollapse() {
|
|
||||||
return !this.sidebar.opened;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,94 +1,112 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
|
<el-scrollbar
|
||||||
|
ref="scrollContainer"
|
||||||
|
:vertical="false"
|
||||||
|
class="scroll-container"
|
||||||
|
@wheel.prevent="handleScroll"
|
||||||
|
>
|
||||||
<slot />
|
<slot />
|
||||||
</el-scrollbar>
|
</el-scrollbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
const tagAndTagSpacing = 4 // tagAndTagSpacing
|
import {
|
||||||
|
ref,
|
||||||
|
getCurrentInstance,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
onBeforeUnmount,
|
||||||
|
} from 'vue';
|
||||||
|
import {useStore} from "vuex";
|
||||||
|
|
||||||
export default {
|
const tagAndTagSpacing = ref(4);
|
||||||
name: 'ScrollPane',
|
const { proxy } = getCurrentInstance();
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
left: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
scrollWrapper() {
|
|
||||||
return this.$refs.scrollContainer.$refs.wrap
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleScroll(e) {
|
|
||||||
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
|
||||||
const $scrollWrapper = this.scrollWrapper
|
|
||||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
|
||||||
},
|
|
||||||
emitScroll() {
|
|
||||||
this.$emit('scroll')
|
|
||||||
},
|
|
||||||
moveToTarget(currentTag) {
|
|
||||||
const $container = this.$refs.scrollContainer.$el
|
|
||||||
const $containerWidth = $container.offsetWidth
|
|
||||||
const $scrollWrapper = this.scrollWrapper
|
|
||||||
const tagList = this.$parent.$refs.tag
|
|
||||||
|
|
||||||
let firstTag = null
|
const scrollWrapper = computed(() => proxy.$refs.scrollContainer.$refs.wrapRef);
|
||||||
let lastTag = null
|
|
||||||
|
|
||||||
// find first tag and last tag
|
onMounted(() => {
|
||||||
if (tagList.length > 0) {
|
scrollWrapper.value.addEventListener('scroll', emitScroll, true)
|
||||||
firstTag = tagList[0]
|
})
|
||||||
lastTag = tagList[tagList.length - 1]
|
onBeforeUnmount(() => {
|
||||||
}
|
scrollWrapper.value.removeEventListener('scroll', emitScroll)
|
||||||
|
})
|
||||||
|
|
||||||
if (firstTag === currentTag) {
|
function handleScroll(e) {
|
||||||
$scrollWrapper.scrollLeft = 0
|
const eventDelta = e.wheelDelta || -e.deltaY * 40
|
||||||
} else if (lastTag === currentTag) {
|
const $scrollWrapper = scrollWrapper.value;
|
||||||
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
|
||||||
} else {
|
}
|
||||||
// find preTag and nextTag
|
const emits = defineEmits()
|
||||||
const currentIndex = tagList.findIndex(item => item === currentTag)
|
const emitScroll = () => {
|
||||||
const prevTag = tagList[currentIndex - 1]
|
emits('scroll')
|
||||||
const nextTag = tagList[currentIndex + 1]
|
}
|
||||||
|
|
||||||
// the tag's offsetLeft after of nextTag
|
const store = useStore();
|
||||||
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
|
const visitedViews = computed(() => store.state.tagsView.visitedViews);
|
||||||
|
|
||||||
// the tag's offsetLeft before of prevTag
|
function moveToTarget(currentTag) {
|
||||||
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
|
const $container = proxy.$refs.scrollContainer.$el
|
||||||
|
const $containerWidth = $container.offsetWidth
|
||||||
|
const $scrollWrapper = scrollWrapper.value;
|
||||||
|
|
||||||
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
let firstTag = null
|
||||||
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
let lastTag = null
|
||||||
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
|
||||||
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
|
// find first tag and last tag
|
||||||
|
if (visitedViews.value.length > 0) {
|
||||||
|
firstTag = visitedViews.value[0]
|
||||||
|
lastTag = visitedViews.value[visitedViews.value.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstTag === currentTag) {
|
||||||
|
$scrollWrapper.scrollLeft = 0
|
||||||
|
} else if (lastTag === currentTag) {
|
||||||
|
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
|
||||||
|
} else {
|
||||||
|
const tagListDom = document.getElementsByClassName('tags-view-item');
|
||||||
|
const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
|
||||||
|
let prevTag = null
|
||||||
|
let nextTag = null
|
||||||
|
for (const k in tagListDom) {
|
||||||
|
if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
|
||||||
|
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
|
||||||
|
prevTag = tagListDom[k];
|
||||||
|
}
|
||||||
|
if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
|
||||||
|
nextTag = tagListDom[k];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the tag's offsetLeft after of nextTag
|
||||||
|
const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value
|
||||||
|
|
||||||
|
// the tag's offsetLeft before of prevTag
|
||||||
|
const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value
|
||||||
|
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
|
||||||
|
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
|
||||||
|
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
|
||||||
|
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
moveToTarget,
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang='scss' scoped>
|
||||||
.scroll-container {
|
.scroll-container {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
::v-deep {
|
:deep(.el-scrollbar__bar) {
|
||||||
.el-scrollbar__bar {
|
bottom: 0px;
|
||||||
bottom: 0px;
|
}
|
||||||
}
|
:deep(.el-scrollbar__wrap) {
|
||||||
.el-scrollbar__wrap {
|
height: 49px;
|
||||||
height: 49px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,249 +1,245 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="tags-view-container" class="tags-view-container">
|
<div id="tags-view-container" class="tags-view-container">
|
||||||
<scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
|
<scroll-pane ref="scrollPaneRef" class="tags-view-wrapper" @scroll="handleScroll">
|
||||||
<router-link
|
<router-link
|
||||||
v-for="tag in visitedViews"
|
v-for="tag in visitedViews"
|
||||||
ref="tag"
|
|
||||||
:key="tag.path"
|
:key="tag.path"
|
||||||
:class="isActive(tag)?'active':''"
|
:data-path="tag.path"
|
||||||
|
:class="isActive(tag) ? 'active' : ''"
|
||||||
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
|
||||||
tag="span"
|
|
||||||
class="tags-view-item"
|
class="tags-view-item"
|
||||||
:style="activeStyle(tag)"
|
:style="activeStyle(tag)"
|
||||||
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
|
@click.middle="!isAffix(tag) ? closeSelectedTag(tag) : ''"
|
||||||
@contextmenu.prevent.native="openMenu(tag,$event)"
|
@contextmenu.prevent="openMenu(tag, $event)"
|
||||||
>
|
>
|
||||||
{{ tag.title }}
|
{{ tag.title }}
|
||||||
<span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
|
<span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
|
||||||
|
<close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
|
||||||
|
</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</scroll-pane>
|
</scroll-pane>
|
||||||
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
|
<ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
|
||||||
<li @click="refreshSelectedTag(selectedTag)"><i class="el-icon-refresh-right"></i> 刷新页面</li>
|
<li @click="refreshSelectedTag(selectedTag)">
|
||||||
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><i class="el-icon-close"></i> 关闭当前</li>
|
<refresh-right style="width: 1em; height: 1em;" /> 刷新页面
|
||||||
<li @click="closeOthersTags"><i class="el-icon-circle-close"></i> 关闭其他</li>
|
</li>
|
||||||
<li v-if="!isFirstView()" @click="closeLeftTags"><i class="el-icon-back"></i> 关闭左侧</li>
|
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">
|
||||||
<li v-if="!isLastView()" @click="closeRightTags"><i class="el-icon-right"></i> 关闭右侧</li>
|
<close style="width: 1em; height: 1em;" /> 关闭当前
|
||||||
<li @click="closeAllTags(selectedTag)"><i class="el-icon-circle-close"></i> 全部关闭</li>
|
</li>
|
||||||
|
<li @click="closeOthersTags">
|
||||||
|
<circle-close style="width: 1em; height: 1em;" /> 关闭其他
|
||||||
|
</li>
|
||||||
|
<li v-if="!isFirstView()" @click="closeLeftTags">
|
||||||
|
<back style="width: 1em; height: 1em;" /> 关闭左侧
|
||||||
|
</li>
|
||||||
|
<li v-if="!isLastView()" @click="closeRightTags">
|
||||||
|
<right style="width: 1em; height: 1em;" /> 关闭右侧
|
||||||
|
</li>
|
||||||
|
<li @click="closeAllTags(selectedTag)">
|
||||||
|
<circle-close style="width: 1em; height: 1em;" /> 全部关闭
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import ScrollPane from './ScrollPane'
|
import ScrollPane from './ScrollPane'
|
||||||
import path from 'path'
|
import { getNormalPath } from '@/utils/ruoyi'
|
||||||
|
|
||||||
export default {
|
const visible = ref(false);
|
||||||
components: { ScrollPane },
|
const top = ref(0);
|
||||||
data() {
|
const left = ref(0);
|
||||||
return {
|
const selectedTag = ref({});
|
||||||
visible: false,
|
const affixTags = ref([]);
|
||||||
top: 0,
|
const scrollPaneRef = ref(null);
|
||||||
left: 0,
|
|
||||||
selectedTag: {},
|
const { proxy } = getCurrentInstance();
|
||||||
affixTags: []
|
const store = useStore();
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const visitedViews = computed(() => store.state.tagsView.visitedViews);
|
||||||
|
const routes = computed(() => store.state.permission.routes);
|
||||||
|
const theme = computed(() => store.state.settings.theme);
|
||||||
|
|
||||||
|
watch(route, () => {
|
||||||
|
addTags()
|
||||||
|
moveToCurrentTag()
|
||||||
|
})
|
||||||
|
watch(visible, (value) => {
|
||||||
|
if (value) {
|
||||||
|
document.body.addEventListener('click', closeMenu)
|
||||||
|
} else {
|
||||||
|
document.body.removeEventListener('click', closeMenu)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
onMounted(() => {
|
||||||
|
initTags()
|
||||||
|
addTags()
|
||||||
|
})
|
||||||
|
|
||||||
|
function isActive(r) {
|
||||||
|
return r.path === route.path
|
||||||
|
}
|
||||||
|
function activeStyle(tag) {
|
||||||
|
if (!isActive(tag)) return {};
|
||||||
|
return {
|
||||||
|
"background-color": theme.value,
|
||||||
|
"border-color": theme.value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function isAffix(tag) {
|
||||||
|
return tag.meta && tag.meta.affix
|
||||||
|
}
|
||||||
|
function isFirstView() {
|
||||||
|
try {
|
||||||
|
return selectedTag.value.fullPath === visitedViews.value[1].fullPath || selectedTag.value.fullPath === '/index'
|
||||||
|
} catch (err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function isLastView() {
|
||||||
|
try {
|
||||||
|
return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath
|
||||||
|
} catch (err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function filterAffixTags(routes, basePath = '') {
|
||||||
|
let tags = []
|
||||||
|
routes.forEach(route => {
|
||||||
|
if (route.meta && route.meta.affix) {
|
||||||
|
const tagPath = getNormalPath(basePath + '/' + route.path)
|
||||||
|
tags.push({
|
||||||
|
fullPath: tagPath,
|
||||||
|
path: tagPath,
|
||||||
|
name: route.name,
|
||||||
|
meta: { ...route.meta }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
if (route.children) {
|
||||||
computed: {
|
const tempTags = filterAffixTags(route.children, route.path)
|
||||||
visitedViews() {
|
if (tempTags.length >= 1) {
|
||||||
return this.$store.state.tagsView.visitedViews
|
tags = [...tags, ...tempTags]
|
||||||
},
|
|
||||||
routes() {
|
|
||||||
return this.$store.state.permission.routes
|
|
||||||
},
|
|
||||||
theme() {
|
|
||||||
return this.$store.state.settings.theme;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
$route() {
|
|
||||||
this.addTags()
|
|
||||||
this.moveToCurrentTag()
|
|
||||||
},
|
|
||||||
visible(value) {
|
|
||||||
if (value) {
|
|
||||||
document.body.addEventListener('click', this.closeMenu)
|
|
||||||
} else {
|
|
||||||
document.body.removeEventListener('click', this.closeMenu)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
mounted() {
|
return tags
|
||||||
this.initTags()
|
}
|
||||||
this.addTags()
|
function initTags() {
|
||||||
},
|
const res = filterAffixTags(routes.value);
|
||||||
methods: {
|
affixTags.value = res;
|
||||||
isActive(route) {
|
for (const tag of res) {
|
||||||
return route.path === this.$route.path
|
// Must have tag name
|
||||||
},
|
if (tag.name) {
|
||||||
activeStyle(tag) {
|
store.dispatch('tagsView/addVisitedView', tag)
|
||||||
if (!this.isActive(tag)) return {};
|
|
||||||
return {
|
|
||||||
"background-color": this.theme,
|
|
||||||
"border-color": this.theme
|
|
||||||
};
|
|
||||||
},
|
|
||||||
isAffix(tag) {
|
|
||||||
return tag.meta && tag.meta.affix
|
|
||||||
},
|
|
||||||
isFirstView() {
|
|
||||||
try {
|
|
||||||
return this.selectedTag.fullPath === '/index' || this.selectedTag.fullPath === this.visitedViews[1].fullPath
|
|
||||||
} catch (err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isLastView() {
|
|
||||||
try {
|
|
||||||
return this.selectedTag.fullPath === this.visitedViews[this.visitedViews.length - 1].fullPath
|
|
||||||
} catch (err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
filterAffixTags(routes, basePath = '/') {
|
|
||||||
let tags = []
|
|
||||||
routes.forEach(route => {
|
|
||||||
if (route.meta && route.meta.affix) {
|
|
||||||
const tagPath = path.resolve(basePath, route.path)
|
|
||||||
tags.push({
|
|
||||||
fullPath: tagPath,
|
|
||||||
path: tagPath,
|
|
||||||
name: route.name,
|
|
||||||
meta: { ...route.meta }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (route.children) {
|
|
||||||
const tempTags = this.filterAffixTags(route.children, route.path)
|
|
||||||
if (tempTags.length >= 1) {
|
|
||||||
tags = [...tags, ...tempTags]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return tags
|
|
||||||
},
|
|
||||||
initTags() {
|
|
||||||
const affixTags = this.affixTags = this.filterAffixTags(this.routes)
|
|
||||||
for (const tag of affixTags) {
|
|
||||||
// Must have tag name
|
|
||||||
if (tag.name) {
|
|
||||||
this.$store.dispatch('tagsView/addVisitedView', tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
addTags() {
|
|
||||||
const { name } = this.$route
|
|
||||||
if (name) {
|
|
||||||
this.$store.dispatch('tagsView/addView', this.$route)
|
|
||||||
if (this.$route.meta.link) {
|
|
||||||
this.$store.dispatch('tagsView/addIframeView', this.$route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
moveToCurrentTag() {
|
|
||||||
const tags = this.$refs.tag
|
|
||||||
this.$nextTick(() => {
|
|
||||||
for (const tag of tags) {
|
|
||||||
if (tag.to.path === this.$route.path) {
|
|
||||||
this.$refs.scrollPane.moveToTarget(tag)
|
|
||||||
// when query is different then update
|
|
||||||
if (tag.to.fullPath !== this.$route.fullPath) {
|
|
||||||
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
refreshSelectedTag(view) {
|
|
||||||
this.$tab.refreshPage(view);
|
|
||||||
if (this.$route.meta.link) {
|
|
||||||
this.$store.dispatch('tagsView/delIframeView', this.$route)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closeSelectedTag(view) {
|
|
||||||
this.$tab.closePage(view).then(({ visitedViews }) => {
|
|
||||||
if (this.isActive(view)) {
|
|
||||||
this.toLastView(visitedViews, view)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
closeRightTags() {
|
|
||||||
this.$tab.closeRightPage(this.selectedTag).then(visitedViews => {
|
|
||||||
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
|
|
||||||
this.toLastView(visitedViews)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
closeLeftTags() {
|
|
||||||
this.$tab.closeLeftPage(this.selectedTag).then(visitedViews => {
|
|
||||||
if (!visitedViews.find(i => i.fullPath === this.$route.fullPath)) {
|
|
||||||
this.toLastView(visitedViews)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
closeOthersTags() {
|
|
||||||
this.$router.push(this.selectedTag.fullPath).catch(()=>{});
|
|
||||||
this.$tab.closeOtherPage(this.selectedTag).then(() => {
|
|
||||||
this.moveToCurrentTag()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
closeAllTags(view) {
|
|
||||||
this.$tab.closeAllPage().then(({ visitedViews }) => {
|
|
||||||
if (this.affixTags.some(tag => tag.path === this.$route.path)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.toLastView(visitedViews, view)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
toLastView(visitedViews, view) {
|
|
||||||
const latestView = visitedViews.slice(-1)[0]
|
|
||||||
if (latestView) {
|
|
||||||
this.$router.push(latestView.fullPath)
|
|
||||||
} else {
|
|
||||||
// now the default is to redirect to the home page if there is no tags-view,
|
|
||||||
// you can adjust it according to your needs.
|
|
||||||
if (view.name === 'Dashboard') {
|
|
||||||
// to reload home page
|
|
||||||
this.$router.replace({ path: '/redirect' + view.fullPath })
|
|
||||||
} else {
|
|
||||||
this.$router.push('/')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openMenu(tag, e) {
|
|
||||||
const menuMinWidth = 105
|
|
||||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
|
||||||
const offsetWidth = this.$el.offsetWidth // container width
|
|
||||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
|
||||||
const left = e.clientX - offsetLeft + 15 // 15: margin right
|
|
||||||
|
|
||||||
if (left > maxLeft) {
|
|
||||||
this.left = maxLeft
|
|
||||||
} else {
|
|
||||||
this.left = left
|
|
||||||
}
|
|
||||||
|
|
||||||
this.top = e.clientY
|
|
||||||
this.visible = true
|
|
||||||
this.selectedTag = tag
|
|
||||||
},
|
|
||||||
closeMenu() {
|
|
||||||
this.visible = false
|
|
||||||
},
|
|
||||||
handleScroll() {
|
|
||||||
this.closeMenu()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function addTags() {
|
||||||
|
const { name } = route
|
||||||
|
if (name) {
|
||||||
|
store.dispatch('tagsView/addView', route)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
function moveToCurrentTag() {
|
||||||
|
nextTick(() => {
|
||||||
|
for (const r of visitedViews.value) {
|
||||||
|
if (r.path === route.path) {
|
||||||
|
scrollPaneRef.value.moveToTarget(r);
|
||||||
|
// when query is different then update
|
||||||
|
if (r.fullPath !== route.fullPath) {
|
||||||
|
store.dispatch('tagsView/updateVisitedView', route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function refreshSelectedTag(view) {
|
||||||
|
proxy.$tab.refreshPage(view);
|
||||||
|
}
|
||||||
|
function closeSelectedTag(view) {
|
||||||
|
proxy.$tab.closePage(view).then(({ visitedViews }) => {
|
||||||
|
if (isActive(view)) {
|
||||||
|
toLastView(visitedViews, view)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function closeRightTags() {
|
||||||
|
proxy.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
|
||||||
|
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
|
||||||
|
toLastView(visitedViews)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function closeLeftTags() {
|
||||||
|
proxy.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
|
||||||
|
if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
|
||||||
|
toLastView(visitedViews)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function closeOthersTags() {
|
||||||
|
router.push(selectedTag.value).catch(() => { });
|
||||||
|
proxy.$tab.closeOtherPage(selectedTag.value).then(() => {
|
||||||
|
moveToCurrentTag()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function closeAllTags(view) {
|
||||||
|
proxy.$tab.closeAllPage().then(({ visitedViews }) => {
|
||||||
|
if (affixTags.value.some(tag => tag.path === route.path)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
toLastView(visitedViews, view)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
function toLastView(visitedViews, view) {
|
||||||
|
const latestView = visitedViews.slice(-1)[0]
|
||||||
|
if (latestView) {
|
||||||
|
router.push(latestView.fullPath)
|
||||||
|
} else {
|
||||||
|
// now the default is to redirect to the home page if there is no tags-view,
|
||||||
|
// you can adjust it according to your needs.
|
||||||
|
if (view.name === 'Dashboard') {
|
||||||
|
// to reload home page
|
||||||
|
router.replace({ path: '/redirect' + view.fullPath })
|
||||||
|
} else {
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function openMenu(tag, e) {
|
||||||
|
const menuMinWidth = 105
|
||||||
|
const offsetLeft = proxy.$el.getBoundingClientRect().left // container margin left
|
||||||
|
const offsetWidth = proxy.$el.offsetWidth // container width
|
||||||
|
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||||
|
const l = e.clientX - offsetLeft + 15 // 15: margin right
|
||||||
|
|
||||||
|
if (l > maxLeft) {
|
||||||
|
left.value = maxLeft
|
||||||
|
} else {
|
||||||
|
left.value = l
|
||||||
|
}
|
||||||
|
|
||||||
|
top.value = e.clientY
|
||||||
|
visible.value = true
|
||||||
|
selectedTag.value = tag
|
||||||
|
}
|
||||||
|
function closeMenu() {
|
||||||
|
visible.value = false
|
||||||
|
}
|
||||||
|
function handleScroll() {
|
||||||
|
closeMenu()
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang='scss' scoped>
|
||||||
.tags-view-container {
|
.tags-view-container {
|
||||||
height: 34px;
|
height: 34px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-bottom: 1px solid #d8dce5;
|
border-bottom: 1px solid #d8dce5;
|
||||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
|
||||||
.tags-view-wrapper {
|
.tags-view-wrapper {
|
||||||
.tags-view-item {
|
.tags-view-item {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
@ -269,7 +265,7 @@ export default {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: #42b983;
|
border-color: #42b983;
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: "";
|
||||||
background: #fff;
|
background: #fff;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
@ -292,7 +288,7 @@ export default {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: #333;
|
color: #333;
|
||||||
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
|
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||||
li {
|
li {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 7px 16px;
|
padding: 7px 16px;
|
||||||
@ -315,16 +311,18 @@ export default {
|
|||||||
vertical-align: 2px;
|
vertical-align: 2px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
transition: all .3s cubic-bezier(.645, .045, .355, 1);
|
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
|
||||||
transform-origin: 100% 50%;
|
transform-origin: 100% 50%;
|
||||||
&:before {
|
&:before {
|
||||||
transform: scale(.6);
|
transform: scale(0.6);
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
vertical-align: -3px;
|
vertical-align: -3px;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: #b4bccc;
|
background-color: #b4bccc;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
width: 12px !important;
|
||||||
|
height: 12px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
export { default as AppMain } from './AppMain'
|
export { default as AppMain } from './AppMain'
|
||||||
export { default as Navbar } from './Navbar'
|
export { default as Navbar } from './Navbar'
|
||||||
export { default as Settings } from './Settings'
|
export { default as Settings } from './Settings'
|
||||||
export { default as Sidebar } from './Sidebar/index.vue'
|
|
||||||
export { default as TagsView } from './TagsView/index.vue'
|
export { default as TagsView } from './TagsView/index.vue'
|
||||||
|
@ -1,111 +1,105 @@
|
|||||||
<template>
|
<template>
|
||||||
<div :class="classObj" class="app-wrapper" :style="{'--current-color': theme}">
|
<div :class="classObj" class="app-wrapper" :style="{ '--current-color': theme }">
|
||||||
<div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
<div v-if="device === 'mobile' && sidebar.opened" class="drawer-bg" @click="handleClickOutside"/>
|
||||||
<sidebar v-if="!sidebar.hide" class="sidebar-container"/>
|
<sidebar class="sidebar-container" />
|
||||||
<div :class="{hasTagsView:needTagsView,sidebarHide:sidebar.hide}" class="main-container">
|
<div :class="{ hasTagsView: needTagsView }" class="main-container">
|
||||||
<div :class="{'fixed-header':fixedHeader}">
|
<div :class="{ 'fixed-header': fixedHeader }">
|
||||||
<navbar/>
|
<navbar @setLayout="setLayout" />
|
||||||
<tags-view v-if="needTagsView"/>
|
<tags-view v-if="needTagsView" />
|
||||||
</div>
|
</div>
|
||||||
<app-main/>
|
<app-main />
|
||||||
<right-panel>
|
<settings ref="settingRef" />
|
||||||
<settings/>
|
|
||||||
</right-panel>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import RightPanel from '@/components/RightPanel'
|
import { useWindowSize } from '@vueuse/core'
|
||||||
import { AppMain, Navbar, Settings, Sidebar, TagsView } from './components'
|
import Sidebar from './components/Sidebar/index.vue'
|
||||||
import ResizeMixin from './mixin/ResizeHandler'
|
import { AppMain, Navbar, Settings, TagsView } from './components'
|
||||||
import { mapState } from 'vuex'
|
import defaultSettings from '@/settings'
|
||||||
import variables from '@/assets/styles/variables.scss'
|
|
||||||
|
|
||||||
export default {
|
const store = useStore();
|
||||||
name: 'Layout',
|
const theme = computed(() => store.state.settings.theme);
|
||||||
components: {
|
const sideTheme = computed(() => store.state.settings.sideTheme);
|
||||||
AppMain,
|
const sidebar = computed(() => store.state.app.sidebar);
|
||||||
Navbar,
|
const device = computed(() => store.state.app.device);
|
||||||
RightPanel,
|
const needTagsView = computed(() => store.state.settings.tagsView);
|
||||||
Settings,
|
const fixedHeader = computed(() => store.state.settings.fixedHeader);
|
||||||
Sidebar,
|
|
||||||
TagsView
|
const classObj = computed(() => ({
|
||||||
},
|
hideSidebar: !sidebar.value.opened,
|
||||||
mixins: [ResizeMixin],
|
openSidebar: sidebar.value.opened,
|
||||||
computed: {
|
withoutAnimation: sidebar.value.withoutAnimation,
|
||||||
...mapState({
|
mobile: device.value === 'mobile'
|
||||||
theme: state => state.settings.theme,
|
}))
|
||||||
sideTheme: state => state.settings.sideTheme,
|
|
||||||
sidebar: state => state.app.sidebar,
|
const { width, height } = useWindowSize();
|
||||||
device: state => state.app.device,
|
const WIDTH = 992; // refer to Bootstrap's responsive design
|
||||||
needTagsView: state => state.settings.tagsView,
|
|
||||||
fixedHeader: state => state.settings.fixedHeader
|
watchEffect(() => {
|
||||||
}),
|
if (device.value === 'mobile' && sidebar.value.opened) {
|
||||||
classObj() {
|
store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
||||||
return {
|
|
||||||
hideSidebar: !this.sidebar.opened,
|
|
||||||
openSidebar: this.sidebar.opened,
|
|
||||||
withoutAnimation: this.sidebar.withoutAnimation,
|
|
||||||
mobile: this.device === 'mobile'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
variables() {
|
|
||||||
return variables;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleClickOutside() {
|
|
||||||
this.$store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (width.value - 1 < WIDTH) {
|
||||||
|
store.dispatch('app/toggleDevice', 'mobile')
|
||||||
|
store.dispatch('app/closeSideBar', { withoutAnimation: true })
|
||||||
|
} else {
|
||||||
|
store.dispatch('app/toggleDevice', 'desktop')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleClickOutside() {
|
||||||
|
store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
const settingRef = ref(null);
|
||||||
|
function setLayout() {
|
||||||
|
settingRef.value.openSetting();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "~@/assets/styles/mixin.scss";
|
@import "@/assets/styles/mixin.scss";
|
||||||
@import "~@/assets/styles/variables.scss";
|
@import "@/assets/styles/variables.module.scss";
|
||||||
|
|
||||||
.app-wrapper {
|
|
||||||
@include clearfix;
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&.mobile.openSidebar {
|
.app-wrapper {
|
||||||
position: fixed;
|
@include clearfix;
|
||||||
top: 0;
|
position: relative;
|
||||||
}
|
height: 100%;
|
||||||
}
|
width: 100%;
|
||||||
|
|
||||||
.drawer-bg {
|
&.mobile.openSidebar {
|
||||||
background: #000;
|
|
||||||
opacity: 0.3;
|
|
||||||
width: 100%;
|
|
||||||
top: 0;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fixed-header {
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
|
||||||
z-index: 9;
|
|
||||||
width: calc(100% - #{$base-sidebar-width});
|
|
||||||
transition: width 0.28s;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.hideSidebar .fixed-header {
|
.drawer-bg {
|
||||||
width: calc(100% - 54px);
|
background: #000;
|
||||||
}
|
opacity: 0.3;
|
||||||
|
width: 100%;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebarHide .fixed-header {
|
.fixed-header {
|
||||||
width: 100%;
|
position: fixed;
|
||||||
}
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
z-index: 9;
|
||||||
|
width: calc(100% - #{$base-sidebar-width});
|
||||||
|
transition: width 0.28s;
|
||||||
|
}
|
||||||
|
|
||||||
.mobile .fixed-header {
|
.hideSidebar .fixed-header {
|
||||||
width: 100%;
|
width: calc(100% - 54px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mobile .fixed-header {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,45 +0,0 @@
|
|||||||
import store from '@/store'
|
|
||||||
|
|
||||||
const { body } = document
|
|
||||||
const WIDTH = 992 // refer to Bootstrap's responsive design
|
|
||||||
|
|
||||||
export default {
|
|
||||||
watch: {
|
|
||||||
$route(route) {
|
|
||||||
if (this.device === 'mobile' && this.sidebar.opened) {
|
|
||||||
store.dispatch('app/closeSideBar', { withoutAnimation: false })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeMount() {
|
|
||||||
window.addEventListener('resize', this.$_resizeHandler)
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
window.removeEventListener('resize', this.$_resizeHandler)
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
const isMobile = this.$_isMobile()
|
|
||||||
if (isMobile) {
|
|
||||||
store.dispatch('app/toggleDevice', 'mobile')
|
|
||||||
store.dispatch('app/closeSideBar', { withoutAnimation: true })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// use $_ for mixins properties
|
|
||||||
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
|
|
||||||
$_isMobile() {
|
|
||||||
const rect = body.getBoundingClientRect()
|
|
||||||
return rect.width - 1 < WIDTH
|
|
||||||
},
|
|
||||||
$_resizeHandler() {
|
|
||||||
if (!document.hidden) {
|
|
||||||
const isMobile = this.$_isMobile()
|
|
||||||
store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
|
|
||||||
|
|
||||||
if (isMobile) {
|
|
||||||
store.dispatch('app/closeSideBar', { withoutAnimation: true })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,86 +1,86 @@
|
|||||||
import Vue from 'vue'
|
import { createApp } from 'vue'
|
||||||
|
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
import Element from 'element-ui'
|
import ElementPlus from 'element-plus'
|
||||||
import './assets/styles/element-variables.scss'
|
import locale from 'element-plus/lib/locale/lang/zh-cn' // 中文语言
|
||||||
|
|
||||||
import '@/assets/styles/index.scss' // global css
|
import '@/assets/styles/index.scss' // global css
|
||||||
import '@/assets/styles/ruoyi.scss' // ruoyi css
|
// element css
|
||||||
|
import 'element-plus/es/components/message/style/css';
|
||||||
|
import 'element-plus/es/components/message-box/style/css';
|
||||||
|
import 'element-plus/es/components/notification/style/css';
|
||||||
|
import 'element-plus/es/components/loading/style/css';
|
||||||
|
|
||||||
import App from './App'
|
import App from './App'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import directive from './directive' // directive
|
import directive from './directive' // directive
|
||||||
|
|
||||||
|
|
||||||
|
// 注册指令
|
||||||
import plugins from './plugins' // plugins
|
import plugins from './plugins' // plugins
|
||||||
import { download } from '@/utils/request'
|
import { download } from '@/utils/request'
|
||||||
|
|
||||||
import './assets/icons' // icon
|
// svg图标
|
||||||
|
import 'virtual:svg-icons-register'; // 引入svg icon注册脚本
|
||||||
|
import SvgIcon from '@/components/SvgIcon'
|
||||||
|
import elementIcons from '@/components/SvgIcon/svgicon'
|
||||||
|
|
||||||
import './permission' // permission control
|
import './permission' // permission control
|
||||||
import { getDicts } from "@/api/system/dict/data";
|
|
||||||
import { getConfigKey } from "@/api/system/config";
|
import { useDict } from '@/utils/dict'
|
||||||
import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi";
|
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel } from '@/utils/ruoyi'
|
||||||
|
|
||||||
// 分页组件
|
// 分页组件
|
||||||
import Pagination from "@/components/Pagination";
|
import Pagination from '@/components/Pagination'
|
||||||
// 自定义表格工具组件
|
// 自定义表格工具组件
|
||||||
import RightToolbar from "@/components/RightToolbar"
|
import RightToolbar from '@/components/RightToolbar'
|
||||||
// 富文本组件
|
|
||||||
import Editor from "@/components/Editor"
|
|
||||||
// 文件上传组件
|
// 文件上传组件
|
||||||
import FileUpload from "@/components/FileUpload"
|
import FileUpload from "@/components/FileUpload"
|
||||||
// 图片上传组件
|
// 图片上传组件
|
||||||
import ImageUpload from "@/components/ImageUpload"
|
import ImageUpload from "@/components/ImageUpload"
|
||||||
// 图片预览组件
|
// 图片预览组件
|
||||||
import ImagePreview from "@/components/ImagePreview"
|
import ImagePreview from "@/components/ImagePreview"
|
||||||
|
// 自定义树选择组件
|
||||||
|
import TreeSelect from '@/components/TreeSelect'
|
||||||
// 字典标签组件
|
// 字典标签组件
|
||||||
import DictTag from '@/components/DictTag'
|
import DictTag from '@/components/DictTag'
|
||||||
// 头部标签组件
|
|
||||||
import VueMeta from 'vue-meta'
|
const app = createApp(App)
|
||||||
// 字典数据组件
|
|
||||||
import DictData from '@/components/DictData'
|
|
||||||
|
|
||||||
// 全局方法挂载
|
// 全局方法挂载
|
||||||
Vue.prototype.getDicts = getDicts
|
app.config.globalProperties.useDict = useDict
|
||||||
Vue.prototype.getConfigKey = getConfigKey
|
app.config.globalProperties.download = download
|
||||||
Vue.prototype.parseTime = parseTime
|
app.config.globalProperties.parseTime = parseTime
|
||||||
Vue.prototype.resetForm = resetForm
|
app.config.globalProperties.resetForm = resetForm
|
||||||
Vue.prototype.addDateRange = addDateRange
|
app.config.globalProperties.handleTree = handleTree
|
||||||
Vue.prototype.selectDictLabel = selectDictLabel
|
app.config.globalProperties.addDateRange = addDateRange
|
||||||
Vue.prototype.selectDictLabels = selectDictLabels
|
app.config.globalProperties.selectDictLabel = selectDictLabel
|
||||||
Vue.prototype.download = download
|
|
||||||
Vue.prototype.handleTree = handleTree
|
|
||||||
|
|
||||||
// 全局组件挂载
|
// 全局组件挂载
|
||||||
Vue.component('DictTag', DictTag)
|
app.component('DictTag', DictTag)
|
||||||
Vue.component('Pagination', Pagination)
|
app.component('Pagination', Pagination)
|
||||||
Vue.component('RightToolbar', RightToolbar)
|
app.component('TreeSelect', TreeSelect)
|
||||||
Vue.component('Editor', Editor)
|
app.component('FileUpload', FileUpload)
|
||||||
Vue.component('FileUpload', FileUpload)
|
app.component('ImageUpload', ImageUpload)
|
||||||
Vue.component('ImageUpload', ImageUpload)
|
app.component('ImagePreview', ImagePreview)
|
||||||
Vue.component('ImagePreview', ImagePreview)
|
app.component('RightToolbar', RightToolbar)
|
||||||
|
|
||||||
Vue.use(directive)
|
app.use(router)
|
||||||
Vue.use(plugins)
|
app.use(store)
|
||||||
Vue.use(VueMeta)
|
app.use(plugins)
|
||||||
DictData.install()
|
app.use(elementIcons)
|
||||||
|
//app.component('svg-icon', SvgIcon)
|
||||||
|
app.component('SvgIcon', SvgIcon)
|
||||||
|
|
||||||
/**
|
directive(app)
|
||||||
* If you don't want to use mock-server
|
|
||||||
* you want to use MockJs for mock api
|
|
||||||
* you can execute: mockXHR()
|
|
||||||
*
|
|
||||||
* Currently MockJs will be used in the production environment,
|
|
||||||
* please remove it before going online! ! !
|
|
||||||
*/
|
|
||||||
|
|
||||||
Vue.use(Element, {
|
// 使用element-plus 并且设置全局的大小
|
||||||
size: Cookies.get('size') || 'medium' // set element-ui default size
|
app.use(ElementPlus, {
|
||||||
|
locale: locale,
|
||||||
|
// 支持 large、default、small
|
||||||
|
size: Cookies.get('size') || 'default'
|
||||||
})
|
})
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
app.mount('#app')
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#app',
|
|
||||||
router,
|
|
||||||
store,
|
|
||||||
render: h => h(App)
|
|
||||||
})
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import { Message } from 'element-ui'
|
import { ElMessage } from 'element-plus'
|
||||||
import NProgress from 'nprogress'
|
import NProgress from 'nprogress'
|
||||||
import 'nprogress/nprogress.css'
|
import 'nprogress/nprogress.css'
|
||||||
import { getToken } from '@/utils/auth'
|
import { getToken } from '@/utils/auth'
|
||||||
import { isRelogin } from '@/utils/request'
|
import { isHttp } from '@/utils/validate'
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false });
|
||||||
|
|
||||||
const whiteList = ['/login', '/register']
|
const whiteList = ['/login', '/auth-redirect', '/bind', '/register'];
|
||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
NProgress.start()
|
NProgress.start()
|
||||||
@ -20,21 +20,23 @@ router.beforeEach((to, from, next) => {
|
|||||||
NProgress.done()
|
NProgress.done()
|
||||||
} else {
|
} else {
|
||||||
if (store.getters.roles.length === 0) {
|
if (store.getters.roles.length === 0) {
|
||||||
isRelogin.show = true
|
|
||||||
// 判断当前用户是否已拉取完user_info信息
|
// 判断当前用户是否已拉取完user_info信息
|
||||||
store.dispatch('GetInfo').then(() => {
|
store.dispatch('GetInfo').then(() => {
|
||||||
isRelogin.show = false
|
|
||||||
store.dispatch('GenerateRoutes').then(accessRoutes => {
|
store.dispatch('GenerateRoutes').then(accessRoutes => {
|
||||||
// 根据roles权限生成可访问的路由表
|
// 根据roles权限生成可访问的路由表
|
||||||
router.addRoutes(accessRoutes) // 动态添加可访问路由表
|
accessRoutes.forEach(route => {
|
||||||
|
if (!isHttp(route.path)) {
|
||||||
|
router.addRoute(route) // 动态添加可访问路由表
|
||||||
|
}
|
||||||
|
})
|
||||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
|
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
store.dispatch('LogOut').then(() => {
|
store.dispatch('LogOut').then(() => {
|
||||||
Message.error(err)
|
ElMessage.error(err)
|
||||||
next({ path: '/' })
|
next({ path: '/' })
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
@ -1,42 +1,42 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { Message } from 'element-ui'
|
import { ElMessage } from 'element-plus'
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
import { getToken } from '@/utils/auth'
|
import { getToken } from '@/utils/auth'
|
||||||
import errorCode from '@/utils/errorCode'
|
import errorCode from '@/utils/errorCode'
|
||||||
import { blobValidate } from "@/utils/ruoyi";
|
import { blobValidate } from '@/utils/ruoyi'
|
||||||
|
|
||||||
const baseURL = process.env.VUE_APP_BASE_API
|
const baseURL = import.meta.env.VITE_APP_BASE_API
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name(name, isDelete = true) {
|
name(name, isDelete = true) {
|
||||||
var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete
|
var url = baseURL + "/common/download?fileName=" + encodeURI(name) + "&delete=" + isDelete
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: url,
|
url: url,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||||
}).then((res) => {
|
}).then(async (res) => {
|
||||||
const isBlob = blobValidate(res.data);
|
const isLogin = await blobValidate(res.data);
|
||||||
if (isBlob) {
|
if (isLogin) {
|
||||||
const blob = new Blob([res.data])
|
const blob = new Blob([res.data])
|
||||||
this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
|
this.saveAs(blob, decodeURI(res.headers['download-filename']))
|
||||||
} else {
|
} else {
|
||||||
this.printErrMsg(res.data);
|
this.printErrMsg(res.data);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
resource(resource) {
|
resource(resource) {
|
||||||
var url = baseURL + "/common/download/resource?resource=" + encodeURIComponent(resource);
|
var url = baseURL + "/common/download/resource?resource=" + encodeURI(resource);
|
||||||
axios({
|
axios({
|
||||||
method: 'get',
|
method: 'get',
|
||||||
url: url,
|
url: url,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||||
}).then((res) => {
|
}).then(async (res) => {
|
||||||
const isBlob = blobValidate(res.data);
|
const isLogin = await blobValidate(res.data);
|
||||||
if (isBlob) {
|
if (isLogin) {
|
||||||
const blob = new Blob([res.data])
|
const blob = new Blob([res.data])
|
||||||
this.saveAs(blob, decodeURIComponent(res.headers['download-filename']))
|
this.saveAs(blob, decodeURI(res.headers['download-filename']))
|
||||||
} else {
|
} else {
|
||||||
this.printErrMsg(res.data);
|
this.printErrMsg(res.data);
|
||||||
}
|
}
|
||||||
@ -49,9 +49,9 @@ export default {
|
|||||||
url: url,
|
url: url,
|
||||||
responseType: 'blob',
|
responseType: 'blob',
|
||||||
headers: { 'Authorization': 'Bearer ' + getToken() }
|
headers: { 'Authorization': 'Bearer ' + getToken() }
|
||||||
}).then((res) => {
|
}).then(async (res) => {
|
||||||
const isBlob = blobValidate(res.data);
|
const isLogin = await blobValidate(res.data);
|
||||||
if (isBlob) {
|
if (isLogin) {
|
||||||
const blob = new Blob([res.data], { type: 'application/zip' })
|
const blob = new Blob([res.data], { type: 'application/zip' })
|
||||||
this.saveAs(blob, name)
|
this.saveAs(blob, name)
|
||||||
} else {
|
} else {
|
||||||
@ -66,7 +66,7 @@ export default {
|
|||||||
const resText = await data.text();
|
const resText = await data.text();
|
||||||
const rspObj = JSON.parse(resText);
|
const rspObj = JSON.parse(resText);
|
||||||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||||
Message.error(errMsg);
|
ElMessage.error(errMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,17 +4,15 @@ import cache from './cache'
|
|||||||
import modal from './modal'
|
import modal from './modal'
|
||||||
import download from './download'
|
import download from './download'
|
||||||
|
|
||||||
export default {
|
export default function installPlugins(app){
|
||||||
install(Vue) {
|
// 页签操作
|
||||||
// 页签操作
|
app.config.globalProperties.$tab = tab
|
||||||
Vue.prototype.$tab = tab
|
// 认证对象
|
||||||
// 认证对象
|
app.config.globalProperties.$auth = auth
|
||||||
Vue.prototype.$auth = auth
|
// 缓存对象
|
||||||
// 缓存对象
|
app.config.globalProperties.$cache = cache
|
||||||
Vue.prototype.$cache = cache
|
// 模态框对象
|
||||||
// 模态框对象
|
app.config.globalProperties.$modal = modal
|
||||||
Vue.prototype.$modal = modal
|
// 下载文件
|
||||||
// 下载文件
|
app.config.globalProperties.$download = download
|
||||||
Vue.prototype.$download = download
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,59 @@
|
|||||||
import { Message, MessageBox, Notification, Loading } from 'element-ui'
|
import { ElMessage, ElMessageBox, ElNotification, ElLoading } from 'element-plus'
|
||||||
|
|
||||||
let loadingInstance;
|
let loadingInstance;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 消息提示
|
// 消息提示
|
||||||
msg(content) {
|
msg(content) {
|
||||||
Message.info(content)
|
ElMessage.info(content)
|
||||||
},
|
},
|
||||||
// 错误消息
|
// 错误消息
|
||||||
msgError(content) {
|
msgError(content) {
|
||||||
Message.error(content)
|
ElMessage.error(content)
|
||||||
},
|
},
|
||||||
// 成功消息
|
// 成功消息
|
||||||
msgSuccess(content) {
|
msgSuccess(content) {
|
||||||
Message.success(content)
|
ElMessage.success(content)
|
||||||
},
|
},
|
||||||
// 警告消息
|
// 警告消息
|
||||||
msgWarning(content) {
|
msgWarning(content) {
|
||||||
Message.warning(content)
|
ElMessage.warning(content)
|
||||||
},
|
},
|
||||||
// 弹出提示
|
// 弹出提示
|
||||||
alert(content) {
|
alert(content) {
|
||||||
MessageBox.alert(content, "系统提示")
|
ElMessageBox.alert(content, "系统提示")
|
||||||
},
|
},
|
||||||
// 错误提示
|
// 错误提示
|
||||||
alertError(content) {
|
alertError(content) {
|
||||||
MessageBox.alert(content, "系统提示", { type: 'error' })
|
ElMessageBox.alert(content, "系统提示", { type: 'error' })
|
||||||
},
|
},
|
||||||
// 成功提示
|
// 成功提示
|
||||||
alertSuccess(content) {
|
alertSuccess(content) {
|
||||||
MessageBox.alert(content, "系统提示", { type: 'success' })
|
ElMessageBox.alert(content, "系统提示", { type: 'success' })
|
||||||
},
|
},
|
||||||
// 警告提示
|
// 警告提示
|
||||||
alertWarning(content) {
|
alertWarning(content) {
|
||||||
MessageBox.alert(content, "系统提示", { type: 'warning' })
|
ElMessageBox.alert(content, "系统提示", { type: 'warning' })
|
||||||
},
|
},
|
||||||
// 通知提示
|
// 通知提示
|
||||||
notify(content) {
|
notify(content) {
|
||||||
Notification.info(content)
|
ElNotification.info(content)
|
||||||
},
|
},
|
||||||
// 错误通知
|
// 错误通知
|
||||||
notifyError(content) {
|
notifyError(content) {
|
||||||
Notification.error(content);
|
ElNotification.error(content);
|
||||||
},
|
},
|
||||||
// 成功通知
|
// 成功通知
|
||||||
notifySuccess(content) {
|
notifySuccess(content) {
|
||||||
Notification.success(content)
|
ElNotification.success(content)
|
||||||
},
|
},
|
||||||
// 警告通知
|
// 警告通知
|
||||||
notifyWarning(content) {
|
notifyWarning(content) {
|
||||||
Notification.warning(content)
|
ElNotification.warning(content)
|
||||||
},
|
},
|
||||||
// 确认窗体
|
// 确认窗体
|
||||||
confirm(content) {
|
confirm(content) {
|
||||||
return MessageBox.confirm(content, "系统提示", {
|
return ElMessageBox.confirm(content, "系统提示", {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: "warning",
|
type: "warning",
|
||||||
@ -61,7 +61,7 @@ export default {
|
|||||||
},
|
},
|
||||||
// 提交内容
|
// 提交内容
|
||||||
prompt(content) {
|
prompt(content) {
|
||||||
return MessageBox.prompt(content, "系统提示", {
|
return ElMessageBox.prompt(content, "系统提示", {
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
cancelButtonText: '取消',
|
cancelButtonText: '取消',
|
||||||
type: "warning",
|
type: "warning",
|
||||||
@ -69,10 +69,9 @@ export default {
|
|||||||
},
|
},
|
||||||
// 打开遮罩层
|
// 打开遮罩层
|
||||||
loading(content) {
|
loading(content) {
|
||||||
loadingInstance = Loading.service({
|
loadingInstance = ElLoading.service({
|
||||||
lock: true,
|
lock: true,
|
||||||
text: content,
|
text: content,
|
||||||
spinner: "el-icon-loading",
|
|
||||||
background: "rgba(0, 0, 0, 0.7)",
|
background: "rgba(0, 0, 0, 0.7)",
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import router from '@/router';
|
import router from '@/router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 刷新当前tab页签
|
// 刷新当前tab页签
|
||||||
refreshPage(obj) {
|
refreshPage(obj) {
|
||||||
const { path, query, matched } = router.currentRoute;
|
const { path, query, matched } = router.currentRoute.value;
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
matched.forEach((m) => {
|
matched.forEach((m) => {
|
||||||
if (m.components && m.components.default && m.components.default.name) {
|
if (m.components && m.components.default && m.components.default.name) {
|
||||||
@ -24,7 +24,7 @@ export default {
|
|||||||
},
|
},
|
||||||
// 关闭当前tab页签,打开新页签
|
// 关闭当前tab页签,打开新页签
|
||||||
closeOpenPage(obj) {
|
closeOpenPage(obj) {
|
||||||
store.dispatch("tagsView/delView", router.currentRoute);
|
store.dispatch("tagsView/delView", router.currentRoute.value);
|
||||||
if (obj !== undefined) {
|
if (obj !== undefined) {
|
||||||
return router.push(obj);
|
return router.push(obj);
|
||||||
}
|
}
|
||||||
@ -32,12 +32,8 @@ export default {
|
|||||||
// 关闭指定tab页签
|
// 关闭指定tab页签
|
||||||
closePage(obj) {
|
closePage(obj) {
|
||||||
if (obj === undefined) {
|
if (obj === undefined) {
|
||||||
return store.dispatch('tagsView/delView', router.currentRoute).then(({ visitedViews }) => {
|
return store.dispatch('tagsView/delView', router.currentRoute.value).then(({ lastPath }) => {
|
||||||
const latestView = visitedViews.slice(-1)[0]
|
return router.push(lastPath || '/index');
|
||||||
if (latestView) {
|
|
||||||
return router.push(latestView.fullPath)
|
|
||||||
}
|
|
||||||
return router.push('/');
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return store.dispatch('tagsView/delView', obj);
|
return store.dispatch('tagsView/delView', obj);
|
||||||
@ -48,21 +44,19 @@ export default {
|
|||||||
},
|
},
|
||||||
// 关闭左侧tab页签
|
// 关闭左侧tab页签
|
||||||
closeLeftPage(obj) {
|
closeLeftPage(obj) {
|
||||||
return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute);
|
return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute.value);
|
||||||
},
|
},
|
||||||
// 关闭右侧tab页签
|
// 关闭右侧tab页签
|
||||||
closeRightPage(obj) {
|
closeRightPage(obj) {
|
||||||
return store.dispatch('tagsView/delRightTags', obj || router.currentRoute);
|
return store.dispatch('tagsView/delRightTags', obj || router.currentRoute.value);
|
||||||
},
|
},
|
||||||
// 关闭其他tab页签
|
// 关闭其他tab页签
|
||||||
closeOtherPage(obj) {
|
closeOtherPage(obj) {
|
||||||
return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute);
|
return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute.value);
|
||||||
},
|
},
|
||||||
// 添加tab页签
|
// 打开tab页签
|
||||||
openPage(title, url, params) {
|
openPage(url) {
|
||||||
var obj = { path: url, meta: { title: title } }
|
return router.push(url);
|
||||||
store.dispatch('tagsView/addView', obj);
|
|
||||||
return router.push({ path: url, query: params });
|
|
||||||
},
|
},
|
||||||
// 修改tab页签
|
// 修改tab页签
|
||||||
updatePage(obj) {
|
updatePage(obj) {
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
import Vue from 'vue'
|
import { createWebHistory, createRouter } from 'vue-router'
|
||||||
import Router from 'vue-router'
|
|
||||||
|
|
||||||
Vue.use(Router)
|
|
||||||
|
|
||||||
/* Layout */
|
|
||||||
import Layout from '@/layout'
|
import Layout from '@/layout'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -17,8 +12,6 @@ import Layout from '@/layout'
|
|||||||
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
|
* redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
|
||||||
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
|
* name:'router-name' // 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题
|
||||||
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
|
* query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数
|
||||||
* roles: ['admin', 'common'] // 访问路由的角色权限
|
|
||||||
* permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限
|
|
||||||
* meta : {
|
* meta : {
|
||||||
noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
|
noCache: true // 如果设置为true,则不会被 <keep-alive> 缓存(默认 false)
|
||||||
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
|
title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字
|
||||||
@ -37,7 +30,7 @@ export const constantRoutes = [
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/redirect/:path(.*)',
|
path: '/redirect/:path(.*)',
|
||||||
component: () => import('@/views/redirect')
|
component: () => import('@/views/redirect/index.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -52,7 +45,7 @@ export const constantRoutes = [
|
|||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/404',
|
path: "/:pathMatch(.*)*",
|
||||||
component: () => import('@/views/error/404'),
|
component: () => import('@/views/error/404'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
@ -64,10 +57,10 @@ export const constantRoutes = [
|
|||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
redirect: 'index',
|
redirect: '/index',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index',
|
path: '/index',
|
||||||
component: () => import('@/views/index'),
|
component: () => import('@/views/index'),
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
meta: { title: '首页', icon: 'dashboard', affix: true }
|
meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||||
@ -87,16 +80,11 @@ export const constantRoutes = [
|
|||||||
meta: { title: '个人中心', icon: 'user' }
|
meta: { title: '个人中心', icon: 'user' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
]
|
|
||||||
|
|
||||||
// 动态路由,基于用户权限动态去加载
|
|
||||||
export const dynamicRoutes = [
|
|
||||||
{
|
{
|
||||||
path: '/system/user-auth',
|
path: '/system/user-auth',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
permissions: ['system:user:edit'],
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'role/:userId(\\d+)',
|
path: 'role/:userId(\\d+)',
|
||||||
@ -110,7 +98,6 @@ export const dynamicRoutes = [
|
|||||||
path: '/system/role-auth',
|
path: '/system/role-auth',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
permissions: ['system:role:edit'],
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'user/:roleId(\\d+)',
|
path: 'user/:roleId(\\d+)',
|
||||||
@ -124,7 +111,6 @@ export const dynamicRoutes = [
|
|||||||
path: '/system/dict-data',
|
path: '/system/dict-data',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
permissions: ['system:dict:list'],
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index/:dictId(\\d+)',
|
path: 'index/:dictId(\\d+)',
|
||||||
@ -138,32 +124,91 @@ export const dynamicRoutes = [
|
|||||||
path: '/tool/gen-edit',
|
path: '/tool/gen-edit',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
permissions: ['tool:gen:edit'],
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index/:tableId(\\d+)',
|
path: 'index',
|
||||||
component: () => import('@/views/tool/gen/editTable'),
|
component: () => import('@/views/tool/gen/editTable'),
|
||||||
name: 'GenEdit',
|
name: 'GenEdit',
|
||||||
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
// 防止连续点击多次路由报错
|
// 动态路由,基于用户权限动态去加载
|
||||||
let routerPush = Router.prototype.push;
|
export const dynamicRoutes= [
|
||||||
let routerReplace = Router.prototype.replace;
|
{
|
||||||
// push
|
path: '/system/user-auth',
|
||||||
Router.prototype.push = function push(location) {
|
component: Layout,
|
||||||
return routerPush.call(this, location).catch(err => err)
|
hidden: true,
|
||||||
}
|
permissions: ['system:user:edit'],
|
||||||
// replace
|
children: [
|
||||||
Router.prototype.replace = function push(location) {
|
{
|
||||||
return routerReplace.call(this, location).catch(err => err)
|
path: 'role/:userId(\\d+)',
|
||||||
}
|
component: () => import('@/views/system/user/authRole.vue'),
|
||||||
|
name: 'AuthRole',
|
||||||
|
meta: { title: '分配角色', activeMenu: '/system/user', icon: '' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/role-auth',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
permissions: ['system:role:edit'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'user/:roleId(\\d+)',
|
||||||
|
component: () => import('@/views/system/role/authUser.vue'),
|
||||||
|
name: 'AuthUser',
|
||||||
|
meta: { title: '分配用户', activeMenu: '/system/role', icon: '' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/system/dict-data',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
permissions: ['system:dict:list'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index/:dictId(\\d+)',
|
||||||
|
component: () => import('@/views/system/dict/data.vue'),
|
||||||
|
name: 'Data',
|
||||||
|
meta: { title: '字典数据', activeMenu: '/system/dict', icon: '' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/tool/gen-edit',
|
||||||
|
component: Layout,
|
||||||
|
hidden: true,
|
||||||
|
permissions: ['tool:gen:edit'],
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index/:tableId(\\d+)',
|
||||||
|
component: () => import('@/views/tool/gen/editTable.vue'),
|
||||||
|
name: 'GenEdit',
|
||||||
|
meta: { title: '修改生成配置', activeMenu: '/tool/gen', icon: '' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
export default new Router({
|
/**
|
||||||
mode: 'history', // 去掉url中的#
|
* 创建路由
|
||||||
scrollBehavior: () => ({ y: 0 }),
|
*/
|
||||||
routes: constantRoutes
|
const router = createRouter({
|
||||||
})
|
history: createWebHistory(import.meta.env.VITE_APP_CONTEXT_PATH),
|
||||||
|
routes: constantRoutes,
|
||||||
|
// 刷新时,滚动条位置还原
|
||||||
|
scrollBehavior(to, from, savedPosition) {
|
||||||
|
if (savedPosition) {
|
||||||
|
return savedPosition
|
||||||
|
} else {
|
||||||
|
return { top: 0 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
module.exports = {
|
export default {
|
||||||
|
/**
|
||||||
|
* 网页标题
|
||||||
|
*/
|
||||||
|
title: import.meta.env.VITE_APP_TITLE,
|
||||||
/**
|
/**
|
||||||
* 侧边栏主题 深色主题theme-dark,浅色主题theme-light
|
* 侧边栏主题 深色主题theme-dark,浅色主题theme-light
|
||||||
*/
|
*/
|
||||||
sideTheme: 'theme-dark',
|
sideTheme: 'theme-dark',
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否系统布局配置
|
* 是否系统布局配置
|
||||||
*/
|
*/
|
||||||
|
@ -2,7 +2,6 @@ const getters = {
|
|||||||
sidebar: state => state.app.sidebar,
|
sidebar: state => state.app.sidebar,
|
||||||
size: state => state.app.size,
|
size: state => state.app.size,
|
||||||
device: state => state.app.device,
|
device: state => state.app.device,
|
||||||
dict: state => state.dict.dict,
|
|
||||||
visitedViews: state => state.tagsView.visitedViews,
|
visitedViews: state => state.tagsView.visitedViews,
|
||||||
cachedViews: state => state.tagsView.cachedViews,
|
cachedViews: state => state.tagsView.cachedViews,
|
||||||
token: state => state.user.token,
|
token: state => state.user.token,
|
||||||
|
@ -1,25 +1,21 @@
|
|||||||
import Vue from 'vue'
|
import { createStore } from 'vuex'
|
||||||
import Vuex from 'vuex'
|
|
||||||
import app from './modules/app'
|
import app from './modules/app'
|
||||||
import dict from './modules/dict'
|
|
||||||
import user from './modules/user'
|
import user from './modules/user'
|
||||||
import tagsView from './modules/tagsView'
|
import tagsView from './modules/tagsView'
|
||||||
import permission from './modules/permission'
|
import permission from './modules/permission'
|
||||||
import settings from './modules/settings'
|
import settings from './modules/settings'
|
||||||
import getters from './getters'
|
import getters from './getters'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
const store = createStore({
|
||||||
|
|
||||||
const store = new Vuex.Store({
|
|
||||||
modules: {
|
modules: {
|
||||||
app,
|
app,
|
||||||
dict,
|
|
||||||
user,
|
user,
|
||||||
tagsView,
|
tagsView,
|
||||||
permission,
|
permission,
|
||||||
settings
|
settings
|
||||||
},
|
},
|
||||||
getters
|
getters
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
export default store
|
export default store
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import auth from '@/plugins/auth'
|
|
||||||
import router, { constantRoutes, dynamicRoutes } from '@/router'
|
import router, { constantRoutes, dynamicRoutes } from '@/router'
|
||||||
import { getRouters } from '@/api/menu'
|
import { getRouters } from '@/api/menu'
|
||||||
import Layout from '@/layout/index'
|
import Layout from '@/layout/index'
|
||||||
import ParentView from '@/components/ParentView'
|
import ParentView from '@/components/ParentView'
|
||||||
import InnerLink from '@/layout/components/InnerLink'
|
import InnerLink from '@/layout/components/InnerLink'
|
||||||
|
|
||||||
|
// 匹配views里面所有的.vue文件
|
||||||
|
const modules = import.meta.glob('./../../views/**/*.vue')
|
||||||
|
|
||||||
const permission = {
|
const permission = {
|
||||||
state: {
|
state: {
|
||||||
routes: [],
|
routes: [],
|
||||||
@ -36,15 +38,14 @@ const permission = {
|
|||||||
getRouters().then(res => {
|
getRouters().then(res => {
|
||||||
const sdata = JSON.parse(JSON.stringify(res.data))
|
const sdata = JSON.parse(JSON.stringify(res.data))
|
||||||
const rdata = JSON.parse(JSON.stringify(res.data))
|
const rdata = JSON.parse(JSON.stringify(res.data))
|
||||||
|
const defaultData = JSON.parse(JSON.stringify(res.data))
|
||||||
const sidebarRoutes = filterAsyncRouter(sdata)
|
const sidebarRoutes = filterAsyncRouter(sdata)
|
||||||
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
||||||
const asyncRoutes = filterDynamicRoutes(dynamicRoutes);
|
const defaultRoutes = filterAsyncRouter(defaultData)
|
||||||
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
|
|
||||||
router.addRoutes(asyncRoutes);
|
|
||||||
commit('SET_ROUTES', rewriteRoutes)
|
commit('SET_ROUTES', rewriteRoutes)
|
||||||
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
|
commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
|
||||||
commit('SET_DEFAULT_ROUTES', sidebarRoutes)
|
commit('SET_DEFAULT_ROUTES', sidebarRoutes)
|
||||||
commit('SET_TOPBAR_ROUTES', sidebarRoutes)
|
commit('SET_TOPBAR_ROUTES', defaultRoutes)
|
||||||
resolve(rewriteRoutes)
|
resolve(rewriteRoutes)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -122,12 +123,14 @@ export function filterDynamicRoutes(routes) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const loadView = (view) => {
|
export const loadView = (view) => {
|
||||||
if (process.env.NODE_ENV === 'development') {
|
let res;
|
||||||
return (resolve) => require([`@/views/${view}`], resolve)
|
for (const path in modules) {
|
||||||
} else {
|
const dir = path.split('views/')[1].split('.vue')[0];
|
||||||
// 使用 import 实现生产环境的路由懒加载
|
if (dir === view) {
|
||||||
return () => import(`@/views/${view}`)
|
res = () => modules[path]();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default permission
|
export default permission
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { login, logout, getInfo } from '@/api/login'
|
import { login, logout, getInfo } from '@/api/login'
|
||||||
import { getToken, setToken, removeToken } from '@/utils/auth'
|
import { getToken, setToken, removeToken } from '@/utils/auth'
|
||||||
|
import defAva from '@/assets/images/profile.jpg'
|
||||||
|
|
||||||
const user = {
|
const user = {
|
||||||
state: {
|
state: {
|
||||||
@ -52,7 +53,7 @@ const user = {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
getInfo().then(res => {
|
getInfo().then(res => {
|
||||||
const user = res.user
|
const user = res.user
|
||||||
const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
|
const avatar = (user.avatar == "" || user.avatar == null) ? defAva : import.meta.env.VITE_APP_BASE_API + user.avatar;
|
||||||
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组
|
||||||
commit('SET_ROLES', res.roles)
|
commit('SET_ROLES', res.roles)
|
||||||
commit('SET_PERMISSIONS', res.permissions)
|
commit('SET_PERMISSIONS', res.permissions)
|
||||||
|
17
ruoyi-ui/src/utils/dict.js
Normal file
17
ruoyi-ui/src/utils/dict.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { getDicts } from '@/api/system/dict/data'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取字典数据
|
||||||
|
*/
|
||||||
|
export function useDict(...args) {
|
||||||
|
const res = ref({});
|
||||||
|
return (() => {
|
||||||
|
args.forEach((d, index) => {
|
||||||
|
res.value[d] = [];
|
||||||
|
getDicts(d).then(resp => {
|
||||||
|
res.value[d] = resp.data.map(p => ({ label: p.dictLabel, value: p.dictValue, elTagType: p.listClass }))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return toRefs(res.value);
|
||||||
|
})()
|
||||||
|
}
|
@ -1,82 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import { mergeRecursive } from "@/utils/ruoyi";
|
|
||||||
import DictMeta from './DictMeta'
|
|
||||||
import DictData from './DictData'
|
|
||||||
|
|
||||||
const DEFAULT_DICT_OPTIONS = {
|
|
||||||
types: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @classdesc 字典
|
|
||||||
* @property {Object} label 标签对象,内部属性名为字典类型名称
|
|
||||||
* @property {Object} dict 字段数组,内部属性名为字典类型名称
|
|
||||||
* @property {Array.<DictMeta>} _dictMetas 字典元数据数组
|
|
||||||
*/
|
|
||||||
export default class Dict {
|
|
||||||
constructor() {
|
|
||||||
this.owner = null
|
|
||||||
this.label = {}
|
|
||||||
this.type = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
init(options) {
|
|
||||||
if (options instanceof Array) {
|
|
||||||
options = { types: options }
|
|
||||||
}
|
|
||||||
const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options)
|
|
||||||
if (opts.types === undefined) {
|
|
||||||
throw new Error('need dict types')
|
|
||||||
}
|
|
||||||
const ps = []
|
|
||||||
this._dictMetas = opts.types.map(t => DictMeta.parse(t))
|
|
||||||
this._dictMetas.forEach(dictMeta => {
|
|
||||||
const type = dictMeta.type
|
|
||||||
Vue.set(this.label, type, {})
|
|
||||||
Vue.set(this.type, type, [])
|
|
||||||
if (dictMeta.lazy) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ps.push(loadDict(this, dictMeta))
|
|
||||||
})
|
|
||||||
return Promise.all(ps)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重新加载字典
|
|
||||||
* @param {String} type 字典类型
|
|
||||||
*/
|
|
||||||
reloadDict(type) {
|
|
||||||
const dictMeta = this._dictMetas.find(e => e.type === type)
|
|
||||||
if (dictMeta === undefined) {
|
|
||||||
return Promise.reject(`the dict meta of ${type} was not found`)
|
|
||||||
}
|
|
||||||
return loadDict(this, dictMeta)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载字典
|
|
||||||
* @param {Dict} dict 字典
|
|
||||||
* @param {DictMeta} dictMeta 字典元数据
|
|
||||||
* @returns {Promise}
|
|
||||||
*/
|
|
||||||
function loadDict(dict, dictMeta) {
|
|
||||||
return dictMeta.request(dictMeta)
|
|
||||||
.then(response => {
|
|
||||||
const type = dictMeta.type
|
|
||||||
let dicts = dictMeta.responseConverter(response, dictMeta)
|
|
||||||
if (!(dicts instanceof Array)) {
|
|
||||||
console.error('the return of responseConverter must be Array.<DictData>')
|
|
||||||
dicts = []
|
|
||||||
} else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) {
|
|
||||||
console.error('the type of elements in dicts must be DictData')
|
|
||||||
dicts = []
|
|
||||||
}
|
|
||||||
dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts)
|
|
||||||
dicts.forEach(d => {
|
|
||||||
Vue.set(dict.label[type], d.value, d.label)
|
|
||||||
})
|
|
||||||
return dicts
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
import DictOptions from './DictOptions'
|
|
||||||
import DictData from './DictData'
|
|
||||||
|
|
||||||
export default function(dict, dictMeta) {
|
|
||||||
const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS)
|
|
||||||
const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS)
|
|
||||||
return new DictData(dict[label], dict[value], dict)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 确定字典字段
|
|
||||||
* @param {DictData} dict
|
|
||||||
* @param {...String} fields
|
|
||||||
*/
|
|
||||||
function determineDictField(dict, ...fields) {
|
|
||||||
return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f))
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
/**
|
|
||||||
* @classdesc 字典数据
|
|
||||||
* @property {String} label 标签
|
|
||||||
* @property {*} value 标签
|
|
||||||
* @property {Object} raw 原始数据
|
|
||||||
*/
|
|
||||||
export default class DictData {
|
|
||||||
constructor(label, value, raw) {
|
|
||||||
this.label = label
|
|
||||||
this.value = value
|
|
||||||
this.raw = raw
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
import { mergeRecursive } from "@/utils/ruoyi";
|
|
||||||
import DictOptions from './DictOptions'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @classdesc 字典元数据
|
|
||||||
* @property {String} type 类型
|
|
||||||
* @property {Function} request 请求
|
|
||||||
* @property {String} label 标签字段
|
|
||||||
* @property {String} value 值字段
|
|
||||||
*/
|
|
||||||
export default class DictMeta {
|
|
||||||
constructor(options) {
|
|
||||||
this.type = options.type
|
|
||||||
this.request = options.request
|
|
||||||
this.responseConverter = options.responseConverter
|
|
||||||
this.labelField = options.labelField
|
|
||||||
this.valueField = options.valueField
|
|
||||||
this.lazy = options.lazy === true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析字典元数据
|
|
||||||
* @param {Object} options
|
|
||||||
* @returns {DictMeta}
|
|
||||||
*/
|
|
||||||
DictMeta.parse= function(options) {
|
|
||||||
let opts = null
|
|
||||||
if (typeof options === 'string') {
|
|
||||||
opts = DictOptions.metas[options] || {}
|
|
||||||
opts.type = options
|
|
||||||
} else if (typeof options === 'object') {
|
|
||||||
opts = options
|
|
||||||
}
|
|
||||||
opts = mergeRecursive(DictOptions.metas['*'], opts)
|
|
||||||
return new DictMeta(opts)
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
import { mergeRecursive } from "@/utils/ruoyi";
|
|
||||||
import dictConverter from './DictConverter'
|
|
||||||
|
|
||||||
export const options = {
|
|
||||||
metas: {
|
|
||||||
'*': {
|
|
||||||
/**
|
|
||||||
* 字典请求,方法签名为function(dictMeta: DictMeta): Promise
|
|
||||||
*/
|
|
||||||
request: (dictMeta) => {
|
|
||||||
console.log(`load dict ${dictMeta.type}`)
|
|
||||||
return Promise.resolve([])
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData
|
|
||||||
*/
|
|
||||||
responseConverter,
|
|
||||||
labelField: 'label',
|
|
||||||
valueField: 'value',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* 默认标签字段
|
|
||||||
*/
|
|
||||||
DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'],
|
|
||||||
/**
|
|
||||||
* 默认值字段
|
|
||||||
*/
|
|
||||||
DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'],
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 映射字典
|
|
||||||
* @param {Object} response 字典数据
|
|
||||||
* @param {DictMeta} dictMeta 字典元数据
|
|
||||||
* @returns {DictData}
|
|
||||||
*/
|
|
||||||
function responseConverter(response, dictMeta) {
|
|
||||||
const dicts = response.content instanceof Array ? response.content : response
|
|
||||||
if (dicts === undefined) {
|
|
||||||
console.warn(`no dict data of "${dictMeta.type}" found in the response`)
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
return dicts.map(d => dictConverter(d, dictMeta))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mergeOptions(src) {
|
|
||||||
mergeRecursive(options, src)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default options
|
|
@ -1,33 +0,0 @@
|
|||||||
import Dict from './Dict'
|
|
||||||
import { mergeOptions } from './DictOptions'
|
|
||||||
|
|
||||||
export default function(Vue, options) {
|
|
||||||
mergeOptions(options)
|
|
||||||
Vue.mixin({
|
|
||||||
data() {
|
|
||||||
if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) {
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
const dict = new Dict()
|
|
||||||
dict.owner = this
|
|
||||||
return {
|
|
||||||
dict
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
if (!(this.dict instanceof Dict)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
options.onCreated && options.onCreated(this.dict)
|
|
||||||
this.dict.init(this.$options.dicts).then(() => {
|
|
||||||
options.onReady && options.onReady(this.dict)
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$emit('dictReady', this.dict)
|
|
||||||
if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) {
|
|
||||||
this.$options.methods.onDictReady.call(this, this.dict)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
13
ruoyi-ui/src/utils/dynamicTitle.js
Normal file
13
ruoyi-ui/src/utils/dynamicTitle.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import store from '@/store'
|
||||||
|
import defaultSettings from '@/settings'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 动态修改标题
|
||||||
|
*/
|
||||||
|
export function useDynamicTitle() {
|
||||||
|
if (store.state.settings.dynamicTitle) {
|
||||||
|
document.title = store.state.settings.title + ' - ' + defaultSettings.title;
|
||||||
|
} else {
|
||||||
|
document.title = defaultSettings.title;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { parseTime } from './ruoyi'
|
import { parseTime } from '@/ruoyi'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 表格时间格式化
|
* 表格时间格式化
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { Notification, MessageBox, Message, Loading } from 'element-ui'
|
import { ElNotification , ElMessageBox, ElMessage, ElLoading } from 'element-plus'
|
||||||
import store from '@/store'
|
import store from '@/store'
|
||||||
import { getToken } from '@/utils/auth'
|
import { getToken } from '@/utils/auth'
|
||||||
import errorCode from '@/utils/errorCode'
|
import errorCode from '@/utils/errorCode'
|
||||||
import { tansParams, blobValidate } from "@/utils/ruoyi";
|
import { tansParams, blobValidate } from '@/utils/ruoyi'
|
||||||
import cache from '@/plugins/cache'
|
import cache from '@/plugins/cache'
|
||||||
import { saveAs } from 'file-saver'
|
import { saveAs } from 'file-saver'
|
||||||
|
|
||||||
let downloadLoadingInstance;
|
let downloadLoadingInstance;
|
||||||
// 是否显示重新登录
|
// 是否显示重新登录
|
||||||
export let isRelogin = { show: false };
|
let isReloginShow;
|
||||||
|
|
||||||
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
|
||||||
// 创建axios实例
|
// 创建axios实例
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
// axios中请求配置有baseURL选项,表示请求URL公共部分
|
||||||
baseURL: process.env.VUE_APP_BASE_API,
|
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||||
// 超时
|
// 超时
|
||||||
timeout: 10000
|
timeout: 10000
|
||||||
})
|
})
|
||||||
@ -46,10 +46,10 @@ service.interceptors.request.use(config => {
|
|||||||
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
|
if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
|
||||||
cache.session.setJSON('sessionObj', requestObj)
|
cache.session.setJSON('sessionObj', requestObj)
|
||||||
} else {
|
} else {
|
||||||
const s_url = sessionObj.url; // 请求地址
|
const s_url = sessionObj.url; // 请求地址
|
||||||
const s_data = sessionObj.data; // 请求数据
|
const s_data = sessionObj.data; // 请求数据
|
||||||
const s_time = sessionObj.time; // 请求时间
|
const s_time = sessionObj.time; // 请求时间
|
||||||
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
|
const interval = 1000; // 间隔时间(ms),小于此时间视为重复提交
|
||||||
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
|
if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
|
||||||
const message = '数据正在处理,请勿重复提交';
|
const message = '数据正在处理,请勿重复提交';
|
||||||
console.warn(`[${s_url}]: ` + message)
|
console.warn(`[${s_url}]: ` + message)
|
||||||
@ -61,8 +61,7 @@ service.interceptors.request.use(config => {
|
|||||||
}
|
}
|
||||||
return config
|
return config
|
||||||
}, error => {
|
}, error => {
|
||||||
console.log(error)
|
Promise.reject(error)
|
||||||
Promise.reject(error)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// 响应拦截器
|
// 响应拦截器
|
||||||
@ -72,73 +71,84 @@ service.interceptors.response.use(res => {
|
|||||||
// 获取错误信息
|
// 获取错误信息
|
||||||
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||||
// 二进制数据则直接返回
|
// 二进制数据则直接返回
|
||||||
if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
|
if(res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer'){
|
||||||
return res.data
|
return res.data
|
||||||
}
|
}
|
||||||
if (code === 401) {
|
if (code === 401) {
|
||||||
if (!isRelogin.show) {
|
if (!isReloginShow) {
|
||||||
isRelogin.show = true;
|
isReloginShow = true;
|
||||||
MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
|
ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
|
||||||
isRelogin.show = false;
|
confirmButtonText: '重新登录',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}
|
||||||
|
).then(() => {
|
||||||
|
isReloginShow = false;
|
||||||
store.dispatch('LogOut').then(() => {
|
store.dispatch('LogOut').then(() => {
|
||||||
location.href = '/index';
|
location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
|
||||||
})
|
})
|
||||||
}).catch(() => {
|
}).catch(() => {
|
||||||
isRelogin.show = false;
|
isReloginShow = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
|
||||||
} else if (code === 500) {
|
} else if (code === 500) {
|
||||||
Message({ message: msg, type: 'error' })
|
ElMessage({
|
||||||
return Promise.reject(new Error(msg))
|
message: msg,
|
||||||
} else if (code === 601) {
|
type: 'error'
|
||||||
Message({ message: msg, type: 'warning' })
|
});
|
||||||
return Promise.reject('error')
|
return Promise.reject(new Error(msg));
|
||||||
} else if (code !== 200) {
|
} else if (code !== 200) {
|
||||||
Notification.error({ title: msg })
|
ElNotification.error({
|
||||||
|
title: msg
|
||||||
|
})
|
||||||
return Promise.reject('error')
|
return Promise.reject('error')
|
||||||
} else {
|
} else {
|
||||||
return res.data
|
return Promise.resolve(res.data)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error => {
|
error => {
|
||||||
console.log('err' + error)
|
|
||||||
let { message } = error;
|
let { message } = error;
|
||||||
if (message == "Network Error") {
|
if (message == "Network Error") {
|
||||||
message = "后端接口连接异常";
|
message = "后端接口连接异常";
|
||||||
} else if (message.includes("timeout")) {
|
}
|
||||||
|
else if (message.includes("timeout")) {
|
||||||
message = "系统接口请求超时";
|
message = "系统接口请求超时";
|
||||||
} else if (message.includes("Request failed with status code")) {
|
}
|
||||||
|
else if (message.includes("Request failed with status code")) {
|
||||||
message = "系统接口" + message.substr(message.length - 3) + "异常";
|
message = "系统接口" + message.substr(message.length - 3) + "异常";
|
||||||
}
|
}
|
||||||
Message({ message: message, type: 'error', duration: 5 * 1000 })
|
ElMessage({
|
||||||
|
message: message,
|
||||||
|
type: 'error',
|
||||||
|
duration: 5 * 1000
|
||||||
|
})
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 通用下载方法
|
// 通用下载方法
|
||||||
export function download(url, params, filename, config) {
|
export function download(url, params, filename) {
|
||||||
downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", })
|
downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
|
||||||
return service.post(url, params, {
|
return service.post(url, params, {
|
||||||
transformRequest: [(params) => { return tansParams(params) }],
|
transformRequest: [(params) => { return tansParams(params) }],
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||||
responseType: 'blob',
|
responseType: 'blob'
|
||||||
...config
|
|
||||||
}).then(async (data) => {
|
}).then(async (data) => {
|
||||||
const isBlob = blobValidate(data);
|
const isLogin = await blobValidate(data);
|
||||||
if (isBlob) {
|
if (isLogin) {
|
||||||
const blob = new Blob([data])
|
const blob = new Blob([data])
|
||||||
saveAs(blob, filename)
|
saveAs(blob, filename)
|
||||||
} else {
|
} else {
|
||||||
const resText = await data.text();
|
const resText = await data.text();
|
||||||
const rspObj = JSON.parse(resText);
|
const rspObj = JSON.parse(resText);
|
||||||
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
|
||||||
Message.error(errMsg);
|
ElMessage.error(errMsg);
|
||||||
}
|
}
|
||||||
downloadLoadingInstance.close();
|
downloadLoadingInstance.close();
|
||||||
}).catch((r) => {
|
}).catch((r) => {
|
||||||
console.error(r)
|
console.error(r)
|
||||||
Message.error('下载文件出现错误,请联系管理员!')
|
ElMessage.error('下载文件出现错误,请联系管理员!')
|
||||||
downloadLoadingInstance.close();
|
downloadLoadingInstance.close();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -227,7 +227,28 @@ export function tansParams(params) {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证是否为blob格式
|
// 返回项目路径
|
||||||
export function blobValidate(data) {
|
export function getNormalPath(p) {
|
||||||
return data.type !== 'application/json'
|
if (p.length === 0 || !p || p == 'undefined') {
|
||||||
|
return p
|
||||||
|
};
|
||||||
|
let res = p.replace('//', '/')
|
||||||
|
if (res[res.length - 1] === '/') {
|
||||||
|
return res.slice(0, res.length - 1)
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证是否为blob格式
|
||||||
|
// export function blobValidate(data) {
|
||||||
|
// return data.type !== 'application/json'
|
||||||
|
// }
|
||||||
|
export async function blobValidate(data) {
|
||||||
|
try {
|
||||||
|
const text = await data.text();
|
||||||
|
JSON.parse(text);
|
||||||
|
return false;
|
||||||
|
} catch (error) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* 判断url是否是http或https
|
||||||
|
* @param {string} path
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
export function isHttp(url) {
|
||||||
|
return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="className" :style="{height:height,width:width}" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
require('echarts/theme/macarons') // echarts theme
|
|
||||||
import resize from './mixins/resize'
|
|
||||||
|
|
||||||
const animationDuration = 6000
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [resize],
|
|
||||||
props: {
|
|
||||||
className: {
|
|
||||||
type: String,
|
|
||||||
default: 'chart'
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
default: '100%'
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: String,
|
|
||||||
default: '300px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.initChart()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
if (!this.chart) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.chart.dispose()
|
|
||||||
this.chart = null
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initChart() {
|
|
||||||
this.chart = echarts.init(this.$el, 'macarons')
|
|
||||||
|
|
||||||
this.chart.setOption({
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: { // 坐标轴指示器,坐标轴触发有效
|
|
||||||
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
top: 10,
|
|
||||||
left: '2%',
|
|
||||||
right: '2%',
|
|
||||||
bottom: '3%',
|
|
||||||
containLabel: true
|
|
||||||
},
|
|
||||||
xAxis: [{
|
|
||||||
type: 'category',
|
|
||||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
|
||||||
axisTick: {
|
|
||||||
alignWithLabel: true
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
yAxis: [{
|
|
||||||
type: 'value',
|
|
||||||
axisTick: {
|
|
||||||
show: false
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
series: [{
|
|
||||||
name: 'pageA',
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'vistors',
|
|
||||||
barWidth: '60%',
|
|
||||||
data: [79, 52, 200, 334, 390, 330, 220],
|
|
||||||
animationDuration
|
|
||||||
}, {
|
|
||||||
name: 'pageB',
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'vistors',
|
|
||||||
barWidth: '60%',
|
|
||||||
data: [80, 52, 200, 334, 390, 330, 220],
|
|
||||||
animationDuration
|
|
||||||
}, {
|
|
||||||
name: 'pageC',
|
|
||||||
type: 'bar',
|
|
||||||
stack: 'vistors',
|
|
||||||
barWidth: '60%',
|
|
||||||
data: [30, 52, 200, 334, 390, 330, 220],
|
|
||||||
animationDuration
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,135 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="className" :style="{height:height,width:width}" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
require('echarts/theme/macarons') // echarts theme
|
|
||||||
import resize from './mixins/resize'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [resize],
|
|
||||||
props: {
|
|
||||||
className: {
|
|
||||||
type: String,
|
|
||||||
default: 'chart'
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
default: '100%'
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: String,
|
|
||||||
default: '350px'
|
|
||||||
},
|
|
||||||
autoResize: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
chartData: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
chartData: {
|
|
||||||
deep: true,
|
|
||||||
handler(val) {
|
|
||||||
this.setOptions(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.initChart()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
if (!this.chart) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.chart.dispose()
|
|
||||||
this.chart = null
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initChart() {
|
|
||||||
this.chart = echarts.init(this.$el, 'macarons')
|
|
||||||
this.setOptions(this.chartData)
|
|
||||||
},
|
|
||||||
setOptions({ expectedData, actualData } = {}) {
|
|
||||||
this.chart.setOption({
|
|
||||||
xAxis: {
|
|
||||||
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
|
|
||||||
boundaryGap: false,
|
|
||||||
axisTick: {
|
|
||||||
show: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
left: 10,
|
|
||||||
right: 10,
|
|
||||||
bottom: 20,
|
|
||||||
top: 30,
|
|
||||||
containLabel: true
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: {
|
|
||||||
type: 'cross'
|
|
||||||
},
|
|
||||||
padding: [5, 10]
|
|
||||||
},
|
|
||||||
yAxis: {
|
|
||||||
axisTick: {
|
|
||||||
show: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
data: ['expected', 'actual']
|
|
||||||
},
|
|
||||||
series: [{
|
|
||||||
name: 'expected', itemStyle: {
|
|
||||||
normal: {
|
|
||||||
color: '#FF005A',
|
|
||||||
lineStyle: {
|
|
||||||
color: '#FF005A',
|
|
||||||
width: 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
smooth: true,
|
|
||||||
type: 'line',
|
|
||||||
data: expectedData,
|
|
||||||
animationDuration: 2800,
|
|
||||||
animationEasing: 'cubicInOut'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'actual',
|
|
||||||
smooth: true,
|
|
||||||
type: 'line',
|
|
||||||
itemStyle: {
|
|
||||||
normal: {
|
|
||||||
color: '#3888fa',
|
|
||||||
lineStyle: {
|
|
||||||
color: '#3888fa',
|
|
||||||
width: 2
|
|
||||||
},
|
|
||||||
areaStyle: {
|
|
||||||
color: '#f3f8ff'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data: actualData,
|
|
||||||
animationDuration: 2800,
|
|
||||||
animationEasing: 'quadraticOut'
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,181 +0,0 @@
|
|||||||
<template>
|
|
||||||
<el-row :gutter="40" class="panel-group">
|
|
||||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
|
||||||
<div class="card-panel" @click="handleSetLineChartData('newVisitis')">
|
|
||||||
<div class="card-panel-icon-wrapper icon-people">
|
|
||||||
<svg-icon icon-class="peoples" class-name="card-panel-icon" />
|
|
||||||
</div>
|
|
||||||
<div class="card-panel-description">
|
|
||||||
<div class="card-panel-text">
|
|
||||||
访客
|
|
||||||
</div>
|
|
||||||
<count-to :start-val="0" :end-val="102400" :duration="2600" class="card-panel-num" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
|
||||||
<div class="card-panel" @click="handleSetLineChartData('messages')">
|
|
||||||
<div class="card-panel-icon-wrapper icon-message">
|
|
||||||
<svg-icon icon-class="message" class-name="card-panel-icon" />
|
|
||||||
</div>
|
|
||||||
<div class="card-panel-description">
|
|
||||||
<div class="card-panel-text">
|
|
||||||
消息
|
|
||||||
</div>
|
|
||||||
<count-to :start-val="0" :end-val="81212" :duration="3000" class="card-panel-num" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
|
||||||
<div class="card-panel" @click="handleSetLineChartData('purchases')">
|
|
||||||
<div class="card-panel-icon-wrapper icon-money">
|
|
||||||
<svg-icon icon-class="money" class-name="card-panel-icon" />
|
|
||||||
</div>
|
|
||||||
<div class="card-panel-description">
|
|
||||||
<div class="card-panel-text">
|
|
||||||
金额
|
|
||||||
</div>
|
|
||||||
<count-to :start-val="0" :end-val="9280" :duration="3200" class="card-panel-num" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="12" :sm="12" :lg="6" class="card-panel-col">
|
|
||||||
<div class="card-panel" @click="handleSetLineChartData('shoppings')">
|
|
||||||
<div class="card-panel-icon-wrapper icon-shopping">
|
|
||||||
<svg-icon icon-class="shopping" class-name="card-panel-icon" />
|
|
||||||
</div>
|
|
||||||
<div class="card-panel-description">
|
|
||||||
<div class="card-panel-text">
|
|
||||||
订单
|
|
||||||
</div>
|
|
||||||
<count-to :start-val="0" :end-val="13600" :duration="3600" class="card-panel-num" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import CountTo from 'vue-count-to'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
CountTo
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleSetLineChartData(type) {
|
|
||||||
this.$emit('handleSetLineChartData', type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.panel-group {
|
|
||||||
margin-top: 18px;
|
|
||||||
|
|
||||||
.card-panel-col {
|
|
||||||
margin-bottom: 32px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-panel {
|
|
||||||
height: 108px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 12px;
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
color: #666;
|
|
||||||
background: #fff;
|
|
||||||
box-shadow: 4px 4px 40px rgba(0, 0, 0, .05);
|
|
||||||
border-color: rgba(0, 0, 0, .05);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
.card-panel-icon-wrapper {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-people {
|
|
||||||
background: #40c9c6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-message {
|
|
||||||
background: #36a3f7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-money {
|
|
||||||
background: #f4516c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shopping {
|
|
||||||
background: #34bfa3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-people {
|
|
||||||
color: #40c9c6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-message {
|
|
||||||
color: #36a3f7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-money {
|
|
||||||
color: #f4516c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-shopping {
|
|
||||||
color: #34bfa3
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-panel-icon-wrapper {
|
|
||||||
float: left;
|
|
||||||
margin: 14px 0 0 14px;
|
|
||||||
padding: 16px;
|
|
||||||
transition: all 0.38s ease-out;
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-panel-icon {
|
|
||||||
float: left;
|
|
||||||
font-size: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-panel-description {
|
|
||||||
float: right;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 26px;
|
|
||||||
margin-left: 0px;
|
|
||||||
|
|
||||||
.card-panel-text {
|
|
||||||
line-height: 18px;
|
|
||||||
color: rgba(0, 0, 0, 0.45);
|
|
||||||
font-size: 16px;
|
|
||||||
margin-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-panel-num {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width:550px) {
|
|
||||||
.card-panel-description {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-panel-icon-wrapper {
|
|
||||||
float: none !important;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
margin: 0 !important;
|
|
||||||
|
|
||||||
.svg-icon {
|
|
||||||
display: block;
|
|
||||||
margin: 14px auto !important;
|
|
||||||
float: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -1,79 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="className" :style="{height:height,width:width}" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
require('echarts/theme/macarons') // echarts theme
|
|
||||||
import resize from './mixins/resize'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [resize],
|
|
||||||
props: {
|
|
||||||
className: {
|
|
||||||
type: String,
|
|
||||||
default: 'chart'
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
default: '100%'
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: String,
|
|
||||||
default: '300px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.initChart()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
if (!this.chart) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.chart.dispose()
|
|
||||||
this.chart = null
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initChart() {
|
|
||||||
this.chart = echarts.init(this.$el, 'macarons')
|
|
||||||
|
|
||||||
this.chart.setOption({
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'item',
|
|
||||||
formatter: '{a} <br/>{b} : {c} ({d}%)'
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
left: 'center',
|
|
||||||
bottom: '10',
|
|
||||||
data: ['Industries', 'Technology', 'Forex', 'Gold', 'Forecasts']
|
|
||||||
},
|
|
||||||
series: [
|
|
||||||
{
|
|
||||||
name: 'WEEKLY WRITE ARTICLES',
|
|
||||||
type: 'pie',
|
|
||||||
roseType: 'radius',
|
|
||||||
radius: [15, 95],
|
|
||||||
center: ['50%', '38%'],
|
|
||||||
data: [
|
|
||||||
{ value: 320, name: 'Industries' },
|
|
||||||
{ value: 240, name: 'Technology' },
|
|
||||||
{ value: 149, name: 'Forex' },
|
|
||||||
{ value: 100, name: 'Gold' },
|
|
||||||
{ value: 59, name: 'Forecasts' }
|
|
||||||
],
|
|
||||||
animationEasing: 'cubicInOut',
|
|
||||||
animationDuration: 2600
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,116 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="className" :style="{height:height,width:width}" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import * as echarts from 'echarts';
|
|
||||||
require('echarts/theme/macarons') // echarts theme
|
|
||||||
import resize from './mixins/resize'
|
|
||||||
|
|
||||||
const animationDuration = 3000
|
|
||||||
|
|
||||||
export default {
|
|
||||||
mixins: [resize],
|
|
||||||
props: {
|
|
||||||
className: {
|
|
||||||
type: String,
|
|
||||||
default: 'chart'
|
|
||||||
},
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
default: '100%'
|
|
||||||
},
|
|
||||||
height: {
|
|
||||||
type: String,
|
|
||||||
default: '300px'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
chart: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.initChart()
|
|
||||||
})
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
if (!this.chart) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
this.chart.dispose()
|
|
||||||
this.chart = null
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
initChart() {
|
|
||||||
this.chart = echarts.init(this.$el, 'macarons')
|
|
||||||
|
|
||||||
this.chart.setOption({
|
|
||||||
tooltip: {
|
|
||||||
trigger: 'axis',
|
|
||||||
axisPointer: { // 坐标轴指示器,坐标轴触发有效
|
|
||||||
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
radar: {
|
|
||||||
radius: '66%',
|
|
||||||
center: ['50%', '42%'],
|
|
||||||
splitNumber: 8,
|
|
||||||
splitArea: {
|
|
||||||
areaStyle: {
|
|
||||||
color: 'rgba(127,95,132,.3)',
|
|
||||||
opacity: 1,
|
|
||||||
shadowBlur: 45,
|
|
||||||
shadowColor: 'rgba(0,0,0,.5)',
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowOffsetY: 15
|
|
||||||
}
|
|
||||||
},
|
|
||||||
indicator: [
|
|
||||||
{ name: 'Sales', max: 10000 },
|
|
||||||
{ name: 'Administration', max: 20000 },
|
|
||||||
{ name: 'Information Techology', max: 20000 },
|
|
||||||
{ name: 'Customer Support', max: 20000 },
|
|
||||||
{ name: 'Development', max: 20000 },
|
|
||||||
{ name: 'Marketing', max: 20000 }
|
|
||||||
]
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
left: 'center',
|
|
||||||
bottom: '10',
|
|
||||||
data: ['Allocated Budget', 'Expected Spending', 'Actual Spending']
|
|
||||||
},
|
|
||||||
series: [{
|
|
||||||
type: 'radar',
|
|
||||||
symbolSize: 0,
|
|
||||||
areaStyle: {
|
|
||||||
normal: {
|
|
||||||
shadowBlur: 13,
|
|
||||||
shadowColor: 'rgba(0,0,0,.2)',
|
|
||||||
shadowOffsetX: 0,
|
|
||||||
shadowOffsetY: 10,
|
|
||||||
opacity: 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data: [
|
|
||||||
{
|
|
||||||
value: [5000, 7000, 12000, 11000, 15000, 14000],
|
|
||||||
name: 'Allocated Budget'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: [4000, 9000, 15000, 15000, 13000, 11000],
|
|
||||||
name: 'Expected Spending'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: [5500, 11000, 12000, 15000, 12000, 12000],
|
|
||||||
name: 'Actual Spending'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
animationDuration: animationDuration
|
|
||||||
}]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
@ -1,56 +0,0 @@
|
|||||||
import { debounce } from '@/utils'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
$_sidebarElm: null,
|
|
||||||
$_resizeHandler: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.initListener()
|
|
||||||
},
|
|
||||||
activated() {
|
|
||||||
if (!this.$_resizeHandler) {
|
|
||||||
// avoid duplication init
|
|
||||||
this.initListener()
|
|
||||||
}
|
|
||||||
|
|
||||||
// when keep-alive chart activated, auto resize
|
|
||||||
this.resize()
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
|
||||||
this.destroyListener()
|
|
||||||
},
|
|
||||||
deactivated() {
|
|
||||||
this.destroyListener()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// use $_ for mixins properties
|
|
||||||
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
|
|
||||||
$_sidebarResizeHandler(e) {
|
|
||||||
if (e.propertyName === 'width') {
|
|
||||||
this.$_resizeHandler()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
initListener() {
|
|
||||||
this.$_resizeHandler = debounce(() => {
|
|
||||||
this.resize()
|
|
||||||
}, 100)
|
|
||||||
window.addEventListener('resize', this.$_resizeHandler)
|
|
||||||
|
|
||||||
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0]
|
|
||||||
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler)
|
|
||||||
},
|
|
||||||
destroyListener() {
|
|
||||||
window.removeEventListener('resize', this.$_resizeHandler)
|
|
||||||
this.$_resizeHandler = null
|
|
||||||
|
|
||||||
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler)
|
|
||||||
},
|
|
||||||
resize() {
|
|
||||||
const { chart } = this
|
|
||||||
chart && chart.resize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="app-container">
|
<div class="app-container">
|
||||||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
|
<el-form :model="queryParams" ref="queryFormRef" :inline="true" v-show="showSearch" label-width="68px">
|
||||||
<el-form-item label="客户姓名" prop="customerName">
|
<el-form-item label="客户姓名" prop="customerName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryParams.customerName"
|
v-model="queryParams.customerName"
|
||||||
@ -20,7 +20,7 @@
|
|||||||
<el-form-item label="客户性别" prop="sex">
|
<el-form-item label="客户性别" prop="sex">
|
||||||
<el-select v-model="queryParams.sex" placeholder="请选择客户性别" clearable>
|
<el-select v-model="queryParams.sex" placeholder="请选择客户性别" clearable>
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in dict.type.sys_user_sex"
|
v-for="dict in sys_user_sex"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
@ -31,13 +31,14 @@
|
|||||||
<el-date-picker clearable
|
<el-date-picker clearable
|
||||||
v-model="queryParams.birthday"
|
v-model="queryParams.birthday"
|
||||||
type="date"
|
type="date"
|
||||||
value-format="yyyy-MM-dd"
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="请选择客户生日">
|
placeholder="请选择客户生日">
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
@ -46,8 +47,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-plus"
|
icon="Plus"
|
||||||
size="mini"
|
|
||||||
@click="handleAdd"
|
@click="handleAdd"
|
||||||
v-hasPermi="['demo:customer:add']"
|
v-hasPermi="['demo:customer:add']"
|
||||||
>新增</el-button>
|
>新增</el-button>
|
||||||
@ -56,8 +56,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="success"
|
type="success"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-edit"
|
icon="Edit"
|
||||||
size="mini"
|
|
||||||
:disabled="single"
|
:disabled="single"
|
||||||
@click="handleUpdate"
|
@click="handleUpdate"
|
||||||
v-hasPermi="['demo:customer:edit']"
|
v-hasPermi="['demo:customer:edit']"
|
||||||
@ -67,8 +66,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="danger"
|
type="danger"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-delete"
|
icon="Delete"
|
||||||
size="mini"
|
|
||||||
:disabled="multiple"
|
:disabled="multiple"
|
||||||
@click="handleDelete"
|
@click="handleDelete"
|
||||||
v-hasPermi="['demo:customer:remove']"
|
v-hasPermi="['demo:customer:remove']"
|
||||||
@ -78,13 +76,12 @@
|
|||||||
<el-button
|
<el-button
|
||||||
type="warning"
|
type="warning"
|
||||||
plain
|
plain
|
||||||
icon="el-icon-download"
|
icon="Download"
|
||||||
size="mini"
|
|
||||||
@click="handleExport"
|
@click="handleExport"
|
||||||
v-hasPermi="['demo:customer:export']"
|
v-hasPermi="['demo:customer:export']"
|
||||||
>导出</el-button>
|
>导出</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="customerList" @selection-change="handleSelectionChange">
|
<el-table v-loading="loading" :data="customerList" @selection-change="handleSelectionChange">
|
||||||
@ -93,29 +90,27 @@
|
|||||||
<el-table-column label="客户姓名" align="center" prop="customerName" />
|
<el-table-column label="客户姓名" align="center" prop="customerName" />
|
||||||
<el-table-column label="手机号码" align="center" prop="phonenumber" />
|
<el-table-column label="手机号码" align="center" prop="phonenumber" />
|
||||||
<el-table-column label="客户性别" align="center" prop="sex">
|
<el-table-column label="客户性别" align="center" prop="sex">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<dict-tag :options="dict.type.sys_user_sex" :value="scope.row.sex"/>
|
<dict-tag :options="sys_user_sex" :value="scope.row.sex"/>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="客户生日" align="center" prop="birthday" width="180">
|
<el-table-column label="客户生日" align="center" prop="birthday" width="180">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<span>{{ parseTime(scope.row.birthday, '{y}-{m}-{d}') }}</span>
|
<span>{{ parseTime(scope.row.birthday, '{y}-{m}-{d}') }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="客户描述" align="center" prop="remark" />
|
<el-table-column label="客户描述" align="center" prop="remark" />
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-edit"
|
icon="Edit"
|
||||||
@click="handleUpdate(scope.row)"
|
@click="handleUpdate(scope.row)"
|
||||||
v-hasPermi="['demo:customer:edit']"
|
v-hasPermi="['demo:customer:edit']"
|
||||||
>修改</el-button>
|
>修改</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
|
||||||
type="text"
|
type="text"
|
||||||
icon="el-icon-delete"
|
icon="Delete"
|
||||||
@click="handleDelete(scope.row)"
|
@click="handleDelete(scope.row)"
|
||||||
v-hasPermi="['demo:customer:remove']"
|
v-hasPermi="['demo:customer:remove']"
|
||||||
>删除</el-button>
|
>删除</el-button>
|
||||||
@ -126,14 +121,14 @@
|
|||||||
<pagination
|
<pagination
|
||||||
v-show="total>0"
|
v-show="total>0"
|
||||||
:total="total"
|
:total="total"
|
||||||
:page.sync="queryParams.pageNum"
|
v-model:page="queryParams.pageNum"
|
||||||
:limit.sync="queryParams.pageSize"
|
v-model:limit="queryParams.pageSize"
|
||||||
@pagination="getList"
|
@pagination="getList"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 添加或修改客户主表(mb)对话框 -->
|
<!-- 添加或修改客户主表(mb)对话框 -->
|
||||||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||||
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
<el-form ref="customerRef" :model="form" :rules="rules" label-width="80px">
|
||||||
<el-form-item label="客户姓名" prop="customerName">
|
<el-form-item label="客户姓名" prop="customerName">
|
||||||
<el-input v-model="form.customerName" placeholder="请输入客户姓名" />
|
<el-input v-model="form.customerName" placeholder="请输入客户姓名" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -143,7 +138,7 @@
|
|||||||
<el-form-item label="客户性别" prop="sex">
|
<el-form-item label="客户性别" prop="sex">
|
||||||
<el-select v-model="form.sex" placeholder="请选择客户性别">
|
<el-select v-model="form.sex" placeholder="请选择客户性别">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in dict.type.sys_user_sex"
|
v-for="dict in sys_user_sex"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
@ -154,7 +149,8 @@
|
|||||||
<el-date-picker clearable
|
<el-date-picker clearable
|
||||||
v-model="form.birthday"
|
v-model="form.birthday"
|
||||||
type="date"
|
type="date"
|
||||||
value-format="yyyy-MM-dd"
|
format="YYYY-MM-DD"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
placeholder="请选择客户生日">
|
placeholder="请选择客户生日">
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -164,40 +160,40 @@
|
|||||||
<el-divider content-position="center">商品子信息</el-divider>
|
<el-divider content-position="center">商品子信息</el-divider>
|
||||||
<el-row :gutter="10" class="mb8">
|
<el-row :gutter="10" class="mb8">
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAddDemoGoods">添加</el-button>
|
<el-button type="primary" icon="Plus" @click="handleAddDemoGoods">添加</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="1.5">
|
<el-col :span="1.5">
|
||||||
<el-button type="danger" icon="el-icon-delete" size="mini" @click="handleDeleteDemoGoods">删除</el-button>
|
<el-button type="danger" icon="Delete" @click="handleDeleteDemoGoods">删除</el-button>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-table :data="demoGoodsList" :row-class-name="rowDemoGoodsIndex" @selection-change="handleDemoGoodsSelectionChange" ref="demoGoods">
|
<el-table :data="demoGoodsList" :row-class-name="rowDemoGoodsIndex" @selection-change="handleDemoGoodsSelectionChange" ref="demoGoods">
|
||||||
<el-table-column type="selection" width="50" align="center" />
|
<el-table-column type="selection" width="50" align="center" />
|
||||||
<el-table-column label="序号" align="center" prop="index" width="50"/>
|
<el-table-column label="序号" align="center" prop="index" width="50"/>
|
||||||
<el-table-column label="商品名称" prop="name" width="150">
|
<el-table-column label="商品名称" prop="name" width="150">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.name" placeholder="请输入商品名称" />
|
<el-input v-model="scope.row.name" placeholder="请输入商品名称" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="商品重量" prop="weight" width="150">
|
<el-table-column label="商品重量" prop="weight" width="150">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.weight" placeholder="请输入商品重量" />
|
<el-input v-model="scope.row.weight" placeholder="请输入商品重量" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="商品价格" prop="price" width="150">
|
<el-table-column label="商品价格" prop="price" width="150">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-input v-model="scope.row.price" placeholder="请输入商品价格" />
|
<el-input v-model="scope.row.price" placeholder="请输入商品价格" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="商品时间" prop="date" width="240">
|
<el-table-column label="商品时间" prop="date" width="240">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-date-picker clearable v-model="scope.row.date" type="date" value-format="yyyy-MM-dd" placeholder="请选择商品时间" />
|
<el-date-picker clearable v-model="scope.row.date" type="date" value-format="YYYY-MM-DD" placeholder="请选择商品时间" />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="商品种类" prop="type" width="150">
|
<el-table-column label="商品种类" prop="type" width="150">
|
||||||
<template slot-scope="scope">
|
<template #default="scope">
|
||||||
<el-select v-model="scope.row.type" placeholder="请选择商品种类">
|
<el-select v-model="scope.row.type" placeholder="请选择商品种类">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="dict in dict.type.sys_goods_type"
|
v-for="dict in sys_goods_type"
|
||||||
:key="dict.value"
|
:key="dict.value"
|
||||||
:label="dict.label"
|
:label="dict.label"
|
||||||
:value="dict.value"
|
:value="dict.value"
|
||||||
@ -207,191 +203,196 @@
|
|||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<template #footer>
|
||||||
<el-button type="primary" @click="submitForm">确 定</el-button>
|
<div class="dialog-footer">
|
||||||
<el-button @click="cancel">取 消</el-button>
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
</div>
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { listCustomer, getCustomer, delCustomer, addCustomer, updateCustomer } from "@/api/demo/customer";
|
import { listCustomer, getCustomer, delCustomer, addCustomer, updateCustomer } from "@/api/demo/customer";
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
const {sys_goods_type, sys_user_sex } = proxy.useDict('sys_goods_type', 'sys_user_sex');
|
||||||
|
|
||||||
export default {
|
// 遮罩层
|
||||||
name: "Customer",
|
const loading = ref(true);
|
||||||
dicts: ['sys_goods_type', 'sys_user_sex'],
|
// 选中数组
|
||||||
data() {
|
const ids = ref([]);
|
||||||
return {
|
// 子表选中数据
|
||||||
// 遮罩层
|
const checkedDemoGoods= ref([])
|
||||||
loading: true,
|
// 非单个禁用
|
||||||
// 选中数组
|
const single = ref(true);
|
||||||
ids: [],
|
// 非多个禁用
|
||||||
// 子表选中数据
|
const multiple = ref(true);
|
||||||
checkedDemoGoods: [],
|
// 总条数
|
||||||
// 非单个禁用
|
const total = ref(0);
|
||||||
single: true,
|
// 是否显示弹出层
|
||||||
// 非多个禁用
|
const open = ref(false);
|
||||||
multiple: true,
|
// 弹出层标题
|
||||||
// 显示搜索条件
|
const title = ref("");
|
||||||
showSearch: true,
|
// 显示搜索条件
|
||||||
// 总条数
|
const showSearch = ref(true);
|
||||||
total: 0,
|
// 客户主表(mb)表格数据
|
||||||
// 客户主表(mb)表格数据
|
const customerList = ref([]);
|
||||||
customerList: [],
|
// 商品子表格数据
|
||||||
// 商品子表格数据
|
const demoGoodsList = ref([]);
|
||||||
demoGoodsList: [],
|
|
||||||
// 弹出层标题
|
const data = reactive({
|
||||||
title: "",
|
// 表单参数
|
||||||
// 是否显示弹出层
|
form: {},
|
||||||
open: false,
|
// 查询参数
|
||||||
// 查询参数
|
queryParams: {
|
||||||
queryParams: {
|
pageNum: 1,
|
||||||
pageNum: 1,
|
pageSize: 10,
|
||||||
pageSize: 10,
|
customerName: undefined,
|
||||||
customerName: null,
|
phonenumber: undefined,
|
||||||
phonenumber: null,
|
sex: undefined,
|
||||||
sex: null,
|
birthday: undefined
|
||||||
birthday: null,
|
|
||||||
},
|
|
||||||
// 表单参数
|
|
||||||
form: {},
|
|
||||||
// 表单校验
|
|
||||||
rules: {
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
created() {
|
// 表单校验
|
||||||
this.getList();
|
rules: {
|
||||||
},
|
customerName: [{ required: true, message: "客户姓名不能为空", trigger: "blur" }],
|
||||||
methods: {
|
phonenumber: [{required: true, message: "手机号码不能为空", trigger: "blur" }],
|
||||||
/** 查询客户主表(mb)列表 */
|
sex: [{ required: true, message: "性别不能为空", trigger: "change" }],
|
||||||
getList() {
|
birthday: [{ required: true, message: "生日不能为空", trigger: "blur" }]
|
||||||
this.loading = true;
|
}
|
||||||
listCustomer(this.queryParams).then(response => {
|
});
|
||||||
this.customerList = response.rows;
|
|
||||||
this.total = response.total;
|
const { form, queryParams, rules } = toRefs(data);
|
||||||
this.loading = false;
|
|
||||||
});
|
/** 查询客户主表(mb)列表 */
|
||||||
},
|
function getList() {
|
||||||
// 取消按钮
|
loading.value = true;
|
||||||
cancel() {
|
listCustomer(queryParams.value).then(response => {
|
||||||
this.open = false;
|
customerList.value = response.rows;
|
||||||
this.reset();
|
total.value = response.total;
|
||||||
},
|
loading.value = false;
|
||||||
// 表单重置
|
});
|
||||||
reset() {
|
}
|
||||||
this.form = {
|
// 取消按钮
|
||||||
customerId: null,
|
function cancel() {
|
||||||
customerName: null,
|
open.value = false;
|
||||||
phonenumber: null,
|
reset();
|
||||||
sex: null,
|
}
|
||||||
birthday: null,
|
// 表单重置
|
||||||
remark: null
|
function reset() {
|
||||||
};
|
form.value = {
|
||||||
this.demoGoodsList = [];
|
customerId: undefined,
|
||||||
this.resetForm("form");
|
customerName: undefined,
|
||||||
},
|
phonenumber: undefined,
|
||||||
/** 搜索按钮操作 */
|
sex: undefined,
|
||||||
handleQuery() {
|
birthday: undefined,
|
||||||
this.queryParams.pageNum = 1;
|
remark: undefined
|
||||||
this.getList();
|
};
|
||||||
},
|
demoGoodsList.value = [];
|
||||||
/** 重置按钮操作 */
|
proxy.resetForm("customerRef");
|
||||||
resetQuery() {
|
}
|
||||||
this.resetForm("queryForm");
|
|
||||||
this.handleQuery();
|
/** 搜索按钮操作 */
|
||||||
},
|
function handleQuery() {
|
||||||
// 多选框选中数据
|
queryParams.value.pageNum = 1;
|
||||||
handleSelectionChange(selection) {
|
getList();
|
||||||
this.ids = selection.map(item => item.customerId)
|
}
|
||||||
this.single = selection.length!==1
|
/** 重置按钮操作 */
|
||||||
this.multiple = !selection.length
|
function resetQuery() {
|
||||||
},
|
proxy.resetForm("queryFormRef");
|
||||||
/** 新增按钮操作 */
|
handleQuery();
|
||||||
handleAdd() {
|
}
|
||||||
this.reset();
|
// 多选框选中数据
|
||||||
this.open = true;
|
function handleSelectionChange(selection) {
|
||||||
this.title = "添加客户主表(mb)";
|
ids.value = selection.map(item => item.customerId)
|
||||||
},
|
single.value = selection.length!==1
|
||||||
/** 修改按钮操作 */
|
multiple.value = !selection.length
|
||||||
handleUpdate(row) {
|
}
|
||||||
this.reset();
|
/** 新增按钮操作 */
|
||||||
const customerId = row.customerId || this.ids
|
function handleAdd() {
|
||||||
getCustomer(customerId).then(response => {
|
reset();
|
||||||
this.form = response.data;
|
open.value = true;
|
||||||
this.demoGoodsList = response.data.demoGoodsList;
|
title.value = "添加客户主表(mb)";
|
||||||
this.open = true;
|
}
|
||||||
this.title = "修改客户主表(mb)";
|
/** 修改按钮操作 */
|
||||||
});
|
function handleUpdate(row) {
|
||||||
},
|
reset();
|
||||||
/** 提交按钮 */
|
const customerId = row.customerId || ids.value
|
||||||
submitForm() {
|
getCustomer(customerId).then(response => {
|
||||||
this.$refs["form"].validate(valid => {
|
form.value = response.data;
|
||||||
if (valid) {
|
demoGoodsList.value = response.data.demoGoodsList;
|
||||||
this.form.demoGoodsList = this.demoGoodsList;
|
open.value = true;
|
||||||
if (this.form.customerId != null) {
|
title.value = "修改客户主表(mb)";
|
||||||
updateCustomer(this.form).then(response => {
|
});
|
||||||
this.$modal.msgSuccess("修改成功");
|
}
|
||||||
this.open = false;
|
/** 删除按钮操作 */
|
||||||
this.getList();
|
function handleDelete(row) {
|
||||||
});
|
const customerIds = row.customerId || ids.value;
|
||||||
} else {
|
proxy.$modal.confirm('是否确认删除客户主表(mb)编号为"' + customerIds + '"的数据项?').then(function() {
|
||||||
addCustomer(this.form).then(response => {
|
return delCustomer(customerIds);
|
||||||
this.$modal.msgSuccess("新增成功");
|
}).then(() => {
|
||||||
this.open = false;
|
getList();
|
||||||
this.getList();
|
proxy.$modal.msgSuccess("删除成功");
|
||||||
});
|
}).catch(() => {});
|
||||||
}
|
}
|
||||||
}
|
/** 提交按钮 */
|
||||||
});
|
function submitForm() {
|
||||||
},
|
proxy.$refs["customerRef"].validate(valid => {
|
||||||
/** 删除按钮操作 */
|
if (valid) {
|
||||||
handleDelete(row) {
|
form.value.demoGoodsList = demoGoodsList;
|
||||||
const customerIds = row.customerId || this.ids;
|
if (form.value.customerId != null) {
|
||||||
this.$modal.confirm('是否确认删除客户主表(mb)编号为"' + customerIds + '"的数据项?').then(function() {
|
updateCustomer(form.value).then(response => {
|
||||||
return delCustomer(customerIds);
|
proxy.$modal.msgSuccess("修改成功");
|
||||||
}).then(() => {
|
open.value = false;
|
||||||
this.getList();
|
getList();
|
||||||
this.$modal.msgSuccess("删除成功");
|
});
|
||||||
}).catch(() => {});
|
|
||||||
},
|
|
||||||
/** 商品子序号 */
|
|
||||||
rowDemoGoodsIndex({ row, rowIndex }) {
|
|
||||||
row.index = rowIndex + 1;
|
|
||||||
},
|
|
||||||
/** 商品子添加按钮操作 */
|
|
||||||
handleAddDemoGoods() {
|
|
||||||
let obj = {};
|
|
||||||
obj.name = "";
|
|
||||||
obj.weight = "";
|
|
||||||
obj.price = "";
|
|
||||||
obj.date = "";
|
|
||||||
obj.type = "";
|
|
||||||
this.demoGoodsList.push(obj);
|
|
||||||
},
|
|
||||||
/** 商品子删除按钮操作 */
|
|
||||||
handleDeleteDemoGoods() {
|
|
||||||
if (this.checkedDemoGoods.length == 0) {
|
|
||||||
this.$modal.msgError("请先选择要删除的商品子数据");
|
|
||||||
} else {
|
} else {
|
||||||
const demoGoodsList = this.demoGoodsList;
|
addCustomer(form.value).then(response => {
|
||||||
const checkedDemoGoods = this.checkedDemoGoods;
|
proxy.$modal.msgSuccess("新增成功");
|
||||||
this.demoGoodsList = demoGoodsList.filter(function(item) {
|
open.value = false;
|
||||||
return checkedDemoGoods.indexOf(item.index) == -1
|
getList();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
|
||||||
/** 复选框选中数据 */
|
|
||||||
handleDemoGoodsSelectionChange(selection) {
|
|
||||||
this.checkedDemoGoods = selection.map(item => item.index)
|
|
||||||
},
|
|
||||||
/** 导出按钮操作 */
|
|
||||||
handleExport() {
|
|
||||||
this.download('demo/customer/export', {
|
|
||||||
...this.queryParams
|
|
||||||
}, `customer_${new Date().getTime()}.xlsx`)
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** 商品子序号 */
|
||||||
|
function rowDemoGoodsIndex({ row, rowIndex }) {
|
||||||
|
row.index = rowIndex + 1;
|
||||||
|
}
|
||||||
|
/** 商品子添加按钮操作 */
|
||||||
|
function handleAddDemoGoods() {
|
||||||
|
let obj = {};
|
||||||
|
obj.name = "";
|
||||||
|
obj.weight = 1;
|
||||||
|
obj.price = 0.0;
|
||||||
|
obj.date = "";
|
||||||
|
obj.type = "";
|
||||||
|
demoGoodsList.value.push(obj);
|
||||||
|
}
|
||||||
|
/** 商品子删除按钮操作 */
|
||||||
|
function handleDeleteDemoGoods() {
|
||||||
|
if (checkedDemoGoods.value.length == 0) {
|
||||||
|
proxy.$modal.msgError("请先选择要删除的商品子数据");
|
||||||
|
} else {
|
||||||
|
const goodsList = demoGoodsList.value;
|
||||||
|
const checkedGoods = checkedDemoGoods.value;
|
||||||
|
demoGoodsList.value = goodsList.filter(function(item) {
|
||||||
|
return checkedGoods.indexOf(item.index) == -1
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
/** 复选框选中数据 */
|
||||||
|
function handleDemoGoodsSelectionChange(selection) {
|
||||||
|
checkedDemoGoods.value = selection.map(item => item.index)
|
||||||
|
}
|
||||||
|
/** 导出按钮操作 */
|
||||||
|
function handleExport() {
|
||||||
|
proxy.download('demo/customer/export', {
|
||||||
|
...queryParams.value
|
||||||
|
}, `customer_${new Date().getTime()}.xlsx`)
|
||||||
|
}
|
||||||
|
|
||||||
|
getList();
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user