fix(登录界面):更新登录界面

This commit is contained in:
林蜗牛 2024-12-27 11:43:58 +08:00
parent ba24afce52
commit 87ac1147bc
16 changed files with 1060 additions and 177 deletions

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_TITLE = Rime-Lin-Vue多租户管理系统
# 开发环境配置
VITE_APP_ENV = 'development'

View File

@ -1,5 +1,5 @@
# 页面标题
VITE_APP_TITLE = RuoYi-Vue-Plus多租户管理系统
VITE_APP_TITLE = Rime-Lin-Vue多租户管理系统
# 生产环境配置
VITE_APP_ENV = 'production'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -0,0 +1,64 @@
import {OllamaForm, OllamaQuery, OllamaVO} from "@/api/test/ollama/types";
import {AxiosPromise} from "axios";
import request from "@/utils/request";
import {DemoForm} from "@/api/demo/demo/types";
/**
*
* @param query
* @returns {*}
*/
export const listOllama = (query?: OllamaQuery): AxiosPromise<OllamaVO[]> => {
return request({
url: '/test/ollama/list',
method: 'get',
params: query
});
};
/**
*
* @param id
* @return {*}
*/
export const getOllama = (id: string | number): AxiosPromise<OllamaVO> => {
return request({
url: '/test/ollama' + id,
method: 'get'
});
};
/**
*
* @param data
*/
export const addOllama = (data: OllamaForm) => {
return request({
url: '/test/ollama',
method: 'post',
data: data
});
};
/**
*
* @param data
*/
export const updateOllama = (data: DemoForm) => {
return request({
url: '/test/ollama',
method: 'put',
data: data
});
};
/**
*
* @param id
*/
export const delOllama = (id: string | number | Array<string | number>) => {
return request({
url: 'test/ollama/' + id,
method: 'delete'
});
};

View File

@ -0,0 +1,12 @@
export interface OllamaVO {
}
export interface OllamaForm extends BaseEntity {
}
export interface OllamaQuery extends PageQuery {
}

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
<title>Group 21</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
<g id="Group-21" transform="translate(77.000000, 73.000000)">
<g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
<ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
<ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
<path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
<path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
<path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
<g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
<ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
<path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
</g>
</g>
<g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
<ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
<ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
<ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
<path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
<g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
<ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
<path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
</g>
<ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
<ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
<path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
</g>
<g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
<ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
<g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
<ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
<path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
</g>
<path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
<ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
<ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
<path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
</g>
<g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
<g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
<circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
<path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
</g>
<circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
<path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
<polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
<path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
<path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
<path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
<circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
<circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
<circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
<circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
<circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -43,13 +43,17 @@
</el-popover>
</div>
</el-tooltip>
<!--
<el-tooltip content="Github" effect="dark" placement="bottom">
<ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
</el-tooltip>
-->
<!--
<el-tooltip :content="$t('navbar.document')" effect="dark" placement="bottom">
<ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
</el-tooltip>
-->
<el-tooltip :content="$t('navbar.full')" effect="dark" placement="bottom">
<screenfull id="screenfull" class="right-menu-item hover-effect" />

View File

@ -34,7 +34,7 @@ defineProps({
}
});
const title = ref('RuoYi-Vue-Plus');
const title = ref('Rime-Lin-Vue');
const settingsStore = useSettingsStore();
const sideTheme = computed(() => settingsStore.sideTheme);
</script>

View File

@ -19,7 +19,6 @@
</template>
<el-empty v-else :description="'消息为空'"></el-empty>
</div>
<div v-if="newsList.length > 0" class="foot-box" @click="onGoToGiteeClick">前往gitee</div>
</div>
</template>
@ -54,9 +53,6 @@ const onNewsClick = (item: any) => {
};
//
const onGoToGiteeClick = () => {
window.open('https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/');
};
onMounted(() => {
nextTick(() => {

View File

@ -2,92 +2,7 @@
<div class="app-container home">
<el-row :gutter="20">
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>RuoYi-Vue-Plus多租户管理系统</h2>
<p>
RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级(不兼容原框架)
<br />
* 前端开发框架 Vue3TSElement Plus<br />
* 后端开发框架 Spring Boot<br />
* 容器框架 Undertow 基于 Netty 的高性能容器<br />
* 权限认证框架 Sa-Token 支持多终端认证系统<br />
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
* 缓存数据库 Redis 适配 6.X 最低 4.X<br />
* 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率<br />
* 数据库框架 p6spy 更强劲的 SQL 分析<br />
* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构<br />
* 序列化框架 Jackson 统一使用 jackson 高效可靠<br />
* Redis客户端 Redisson 性能强劲API丰富<br />
* 分布式限流 Redisson 全局请求IP集群ID 多种限流<br />
* 分布式锁 Lock4j 注解锁工具锁 多种多样<br />
* 分布式幂等 Lock4j 基于分布式锁实现<br />
* 分布式链路追踪 SkyWalking 支持链路追踪网格分析度量聚合可视化<br />
* 分布式任务调度 SnailJob 高性能 高可靠 易扩展<br />
* 文件存储 Minio 本地存储<br />
* 文件存储 七牛阿里腾讯 云存储<br />
* 监控框架 SpringBoot-Admin 全方位服务监控<br />
* 校验框架 Validation 增强接口安全性 严谨性<br />
* Excel框架 Alibaba EasyExcel 性能优异 扩展性强<br />
* 文档框架 SpringDocjavadoc 无注解零入侵基于java注释<br />
* 工具类框架 HutoolLombok 减少代码冗余 增加安全性<br />
* 代码生成器 适配MPSpringDoc规范化代码 一键生成前后端代码<br />
* 部署方式 Docker 容器编排 一键部署业务集群<br />
* 国际化 SpringMessage Spring标准国际化方案<br />
</p>
<p><b>当前版本:</b> <span>v5.2.1</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
<p>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
>更新日志</el-button
>
</p>
</el-col>
<el-col :sm="24" :lg="12" style="padding-left: 20px">
<h2>RuoYi-Cloud-Plus多租户微服务管理系统</h2>
<p>
RuoYi-Cloud-Plus 微服务通用权限管理系统 重写 RuoYi-Cloud 全方位升级(不兼容原框架)
<br />
* 前端开发框架 Vue3TSElement UI<br />
* 后端开发框架 Spring Boot<br />
* 微服务开发框架 Spring CloudSpring Cloud Alibaba<br />
* 容器框架 Undertow 基于 XNIO 的高性能容器<br />
* 权限认证框架 Sa-TokenJwt 支持多终端认证系统<br />
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
* 关系数据库 Oracle 适配 11g 12c<br />
* 关系数据库 PostgreSQL 适配 13 14<br />
* 关系数据库 SQLServer 适配 2017 2019<br />
* 缓存数据库 Redis 适配 6.X 最低 5.X<br />
* 分布式注册中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
* 分布式配置中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
* 服务网关 Spring Cloud Gateway 响应式高性能网关<br />
* 负载均衡 Spring Cloud Loadbalancer 负载均衡处理<br />
* RPC远程调用 Apache Dubbo 原生态使用体验高性能<br />
* 分布式限流熔断 Alibaba Sentinel 无侵入高扩展<br />
* 分布式事务 Alibaba Seata 无侵入高扩展 支持 四种模式<br />
* 分布式消息队列 Apache Kafka 高性能高速度<br />
* 分布式消息队列 Apache RocketMQ 高可用功能多样<br />
* 分布式消息队列 RabbitMQ 支持各种扩展插件功能多样性<br />
* 分布式搜索引擎 ElasticSearch 业界知名<br />
* 分布式链路追踪 Apache SkyWalking 链路追踪网格分析度量聚合可视化<br />
* 分布式日志中心 ELK 业界成熟解决方案<br />
* 分布式监控 PrometheusGrafana 全方位性能监控<br />
* 其余与 Vue 版本一致<br />
</p>
<p><b>当前版本:</b> <span>v2.2.0</span></p>
<p>
<el-tag type="danger">&yen;免费开源</el-tag>
</p>
<p>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button>
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
>更新日志</el-button
>
</p>
<h2>Rime-Lin-Vue多租户管理系统</h2>
</el-col>
</el-row>
<el-divider />

283
src/views/login-bak.txt Normal file
View File

@ -0,0 +1,283 @@
<template>
<div class="login">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">个人测试系统</h3>
<el-form-item v-if="tenantEnabled" prop="tenantId">
<el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
</el-select>
</el-form-item>
<el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码" @keyup.enter="handleLogin">
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item v-if="captchaEnabled" prop="code">
<el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin">
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input>
<div class="login-code">
<img :src="codeUrl" class="login-code-img" @click="getCode" />
</div>
</el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 25px 0">记住密码</el-checkbox>
<el-form-item style="float: right">
<el-button circle title="微信登录" @click="doSocialLogin('wechat_open')">
<svg-icon icon-class="wechat" />
</el-button>
<!-- <el-button circle title="MaxKey登录" @click="doSocialLogin('maxkey')">
<svg-icon icon-class="maxkey" />
</el-button>
<el-button circle title="TopIam登录" @click="doSocialLogin('topiam')">
<svg-icon icon-class="topiam" />
</el-button>
<el-button circle title="Gitee登录" @click="doSocialLogin('gitee')">
<svg-icon icon-class="gitee" />
</el-button>
<el-button circle title="Github登录" @click="doSocialLogin('github')">
<svg-icon icon-class="github" />
</el-button>-->
</el-form-item>
<el-form-item style="width: 100%">
<el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleLogin">
<span v-if="!loading">登 录</span>
<span v-else>登 录 中...</span>
</el-button>
<div v-if="register" style="float: right">
<router-link class="link-type" :to="'/register'">立即注册</router-link>
</div>
</el-form-item>
</el-form>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2024 Linsnaillo All Rights Reserved.</span>
</div>
</div>
</template>
<script setup lang="ts">
import { getCodeImg, getTenantList } from '@/api/login';
import { authBinding } from '@/api/system/social/auth';
import { useUserStore } from '@/store/modules/user';
import { LoginData, TenantVO } from '@/api/types';
import { to } from 'await-to-js';
import { HttpStatus } from '@/enums/RespEnum';
const userStore = useUserStore();
const router = useRouter();
const loginForm = ref<LoginData>({
tenantId: '',
username: '',
password: '',
rememberMe: false,
code: '',
uuid: ''
} as LoginData);
const loginRules: ElFormRules = {
tenantId: [{ required: true, trigger: 'blur', message: '请输入您的租户编号' }],
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
password: [{ required: true, trigger: 'blur', message: '请输入您的密码' }],
code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
};
const codeUrl = ref('');
const loading = ref(false);
// 验证码开关
const captchaEnabled = ref(true);
// 租户开关
const tenantEnabled = ref(true);
// 注册开关
const register = ref(false);
const redirect = ref(undefined);
const loginRef = ref<ElFormInstance>();
// 租户列表
const tenantList = ref<TenantVO[]>([]);
watch(
() => router.currentRoute.value,
(newRoute: any) => {
redirect.value = newRoute.query && newRoute.query.redirect;
},
{ immediate: true }
);
const handleLogin = () => {
loginRef.value?.validate(async (valid: boolean, fields: any) => {
if (valid) {
loading.value = true;
// 勾选了需要记住密码设置在 localStorage 中设置记住用户名和密码
if (loginForm.value.rememberMe) {
localStorage.setItem('tenantId', String(loginForm.value.tenantId));
localStorage.setItem('username', String(loginForm.value.username));
localStorage.setItem('password', String(loginForm.value.password));
localStorage.setItem('rememberMe', String(loginForm.value.rememberMe));
} else {
// 否则移除
localStorage.removeItem('tenantId');
localStorage.removeItem('username');
localStorage.removeItem('password');
localStorage.removeItem('rememberMe');
}
// 调用action的登录方法
const [err] = await to(userStore.login(loginForm.value));
if (!err) {
const redirectUrl = redirect.value || '/';
await router.push(redirectUrl);
loading.value = false;
} else {
loading.value = false;
// 重新获取验证码
if (captchaEnabled.value) {
await getCode();
}
}
} else {
console.log('error submit!', fields);
}
});
};
/**
* 获取验证码
*/
const getCode = async () => {
const res = await getCodeImg();
const { data } = res;
captchaEnabled.value = data.captchaEnabled === undefined ? true : data.captchaEnabled;
if (captchaEnabled.value) {
codeUrl.value = 'data:image/gif;base64,' + data.img;
loginForm.value.uuid = data.uuid;
}
};
const getLoginData = () => {
const tenantId = localStorage.getItem('tenantId');
const username = localStorage.getItem('username');
const password = localStorage.getItem('password');
const rememberMe = localStorage.getItem('rememberMe');
loginForm.value = {
tenantId: tenantId === null ? String(loginForm.value.tenantId) : tenantId,
username: username === null ? String(loginForm.value.username) : username,
password: password === null ? String(loginForm.value.password) : String(password),
rememberMe: rememberMe === null ? false : Boolean(rememberMe)
} as LoginData;
};
/**
* 获取租户列表
*/
const initTenantList = async () => {
const { data } = await getTenantList();
tenantEnabled.value = data.tenantEnabled === undefined ? true : data.tenantEnabled;
if (tenantEnabled.value) {
tenantList.value = data.voList;
if (tenantList.value != null && tenantList.value.length !== 0) {
loginForm.value.tenantId = tenantList.value[0].tenantId;
}
}
};
/**
* 第三方登录
* @param type
*/
const doSocialLogin = (type: string) => {
authBinding(type, loginForm.value.tenantId).then((res: any) => {
if (res.code === HttpStatus.SUCCESS) {
// 获取授权地址跳转
window.location.href = res.data;
} else {
ElMessage.error(res.msg);
}
});
};
onMounted(() => {
getCode();
initTenantList();
getLoginData();
});
</script>
<style lang="scss" scoped>
.login {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
background: linear-gradient(135deg, #f0f4f8 30%, #a1c4fd 50%, #3658ad 100%);
background-image: url('../assets/images/background.svg'), linear-gradient(135deg, #f0f4f8 30%, #a1c4fd 50%, #3658ADFF 100%);
background-size: cover;
}
.title {
margin: 0px auto 30px auto;
text-align: center;
color: #707070;
}
.login-form {
border-radius: 6px;
background: #ffffff;
width: 400px;
padding: 25px 25px 5px 25px;
.el-input {
height: 40px;
input {
height: 40px;
}
}
.input-icon {
height: 39px;
width: 14px;
margin-left: 0px;
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 40px;
float: right;
img {
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
color: #fff;
font-family: Arial, serif;
font-size: 12px;
letter-spacing: 1px;
}
.login-code-img {
height: 40px;
padding-left: 12px;
}
</style>

View File

@ -1,71 +1,146 @@
<template>
<div class="login">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3>
<el-form-item v-if="tenantEnabled" prop="tenantId">
<el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
</el-select>
</el-form-item>
<el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码" @keyup.enter="handleLogin">
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item v-if="captchaEnabled" prop="code">
<el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin">
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input>
<div class="login-code">
<img :src="codeUrl" class="login-code-img" @click="getCode" />
<div class="container-demo">
<div class="login-container">
<div class="form-box login">
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
<h3>登录</h3>
<el-form-item v-if="tenantEnabled" prop="tenantId" class="input-box">
<el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
</el-select>
</el-form-item>
<el-form-item prop="username" class="input-box">
<el-input v-model="loginForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item prop="password" class="input-box">
<el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码" @keyup.enter="handleLogin">
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
<el-form-item v-if="captchaEnabled" prop="code" class="input-box">
<el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin">
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input>
<div class="login-code">
<img :src="codeUrl" class="login-code-img" @click="getCode" />
</div>
</el-form-item>
<el-form-item style="width: 100%">
<el-button class="btn" :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleLogin">
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
</el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 2px 0">记住密码</el-checkbox>
<p>使用第三方平台登录</p>
<div class="social-icons">
<el-form-item style="float: right">
<el-button circle title="微信登录" @click="doSocialLogin('wechat_open')">
<svg-icon icon-class="wechat" />
</el-button>
<el-button circle title="MaxKey登录" @click="doSocialLogin('maxkey')">
<svg-icon icon-class="maxkey" />
</el-button>
<el-button circle title="TopIam登录" @click="doSocialLogin('topiam')">
<svg-icon icon-class="topiam" />
</el-button>
<el-button circle title="Gitee登录" @click="doSocialLogin('gitee')">
<svg-icon icon-class="gitee" />
</el-button>
<el-button circle title="Github登录" @click="doSocialLogin('github')">
<svg-icon icon-class="github" />
</el-button>
</el-form-item>
</div>
</el-form>
</div>
<div class="form-box register">
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
<h3>注册</h3>
<div class="input-box">
<el-form-item v-if="tenantEnabled" prop="tenantId">
<el-select v-model="registerForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option>
<template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
</el-select>
</el-form-item>
</div>
<div class="input-box">
<el-form-item prop="username">
<el-input v-model="registerForm.username" type="text" size="large" auto-complete="off" placeholder="账号">
<template #prefix><svg-icon icon-class="user" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
</div>
<div class="input-box">
<el-form-item prop="password">
<el-input v-model="registerForm.password" type="password" size="large" auto-complete="off" placeholder="密码" @keyup.enter="handleRegister">
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
</div>
<div class="input-box">
<el-form-item prop="confirmPassword">
<el-input
v-model="registerForm.confirmPassword"
type="password"
size="large"
auto-complete="off"
placeholder="确认密码"
@keyup.enter="handleRegister"
>
<template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
</el-input>
</el-form-item>
</div>
<div class="input-box">
<el-form-item v-if="captchaEnabled" prop="code">
<el-input v-model="registerForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleRegister">
<template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
</el-input>
<div class="register-code">
<img :src="codeUrl" class="register-code-img" @click="getCode" />
</div>
</el-form-item>
</div>
<el-form-item style="width: 100%">
<el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleRegister" class="btn">
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
<div class="toggle-box">
<div class="toggle-panel toggle-left">
<h1>Hello, Welcome</h1>
<p>还没账号?</p>
<button class="btn register-btn">注册</button>
</div>
</el-form-item>
<el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 25px 0">记住密码</el-checkbox>
<el-form-item style="float: right">
<el-button circle title="微信登录" @click="doSocialLogin('wechat')">
<svg-icon icon-class="wechat" />
</el-button>
<el-button circle title="MaxKey登录" @click="doSocialLogin('maxkey')">
<svg-icon icon-class="maxkey" />
</el-button>
<el-button circle title="TopIam登录" @click="doSocialLogin('topiam')">
<svg-icon icon-class="topiam" />
</el-button>
<el-button circle title="Gitee登录" @click="doSocialLogin('gitee')">
<svg-icon icon-class="gitee" />
</el-button>
<el-button circle title="Github登录" @click="doSocialLogin('github')">
<svg-icon icon-class="github" />
</el-button>
</el-form-item>
<el-form-item style="width: 100%">
<el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleLogin">
<span v-if="!loading"> </span>
<span v-else> 中...</span>
</el-button>
<div v-if="register" style="float: right">
<router-link class="link-type" :to="'/register'">立即注册</router-link>
<div class="toggle-panel toggle-right">
<h1>Welcome Back!</h1>
<p>已经有账号了</p>
<button class="btn login-btn">登录</button>
</div>
</el-form-item>
</el-form>
</div>
</div>
<!-- 底部 -->
<div class="el-login-footer">
<span>Copyright © 2018-2024 疯狂的狮子Li All Rights Reserved.</span>
<span>Copyright © 2018-2024 Linsnaillo All Rights Reserved.</span>
</div>
</div>
</template>
<script setup lang="ts">
import { getCodeImg, getTenantList } from '@/api/login';
import { getCodeImg, register, getTenantList } from '@/api/login';
import { authBinding } from '@/api/system/social/auth';
import { useUserStore } from '@/store/modules/user';
import { LoginData, TenantVO } from '@/api/types';
import {LoginData, RegisterForm, TenantVO} from '@/api/types';
import { to } from 'await-to-js';
import { HttpStatus } from '@/enums/RespEnum';
@ -73,14 +148,24 @@ const userStore = useUserStore();
const router = useRouter();
const loginForm = ref<LoginData>({
tenantId: '000000',
username: 'admin',
password: 'admin123',
tenantId: '',
username: '',
password: '',
rememberMe: false,
code: '',
uuid: ''
} as LoginData);
const registerForm = ref<RegisterForm>({
tenantId: '',
username: '',
password: '',
confirmPassword: '',
code: '',
uuid: '',
userType: 'sys_user'
});
const loginRules: ElFormRules = {
tenantId: [{ required: true, trigger: 'blur', message: '请输入您的租户编号' }],
username: [{ required: true, trigger: 'blur', message: '请输入您的账号' }],
@ -88,6 +173,32 @@ const loginRules: ElFormRules = {
code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
};
const equalToPassword = (rule: any, value: string, callback: any) => {
if (registerForm.value.password !== value) {
callback(new Error('两次输入的密码不一致'));
} else {
callback();
}
};
const registerRules: ElFormRules = {
tenantId: [{ required: true, trigger: 'blur', message: '请输入您的租户编号' }],
username: [
{ required: true, trigger: 'blur', message: '请输入您的账号' },
{ min: 2, max: 20, message: '用户账号长度必须介于 2 和 20 之间', trigger: 'blur' }
],
password: [
{ required: true, trigger: 'blur', message: '请输入您的密码' },
{ min: 5, max: 20, message: '用户密码长度必须介于 5 和 20 之间', trigger: 'blur' },
{ pattern: /^[^<>"'|\\]+$/, message: '不能包含非法字符:< > " \' \\\ |', trigger: 'blur' }
],
confirmPassword: [
{ required: true, trigger: 'blur', message: '请再次输入您的密码' },
{ required: true, validator: equalToPassword, trigger: 'blur' }
],
code: [{ required: true, trigger: 'change', message: '请输入验证码' }]
};
const codeUrl = ref('');
const loading = ref(false);
//
@ -95,10 +206,12 @@ const captchaEnabled = ref(true);
//
const tenantEnabled = ref(true);
//
const register = ref(false);
const redirect = ref(undefined);
//
const loginRef = ref<ElFormInstance>();
//
const registerRef = ref<ElFormInstance>();
//
const tenantList = ref<TenantVO[]>([]);
@ -146,6 +259,28 @@ const handleLogin = () => {
});
};
const handleRegister = () => {
registerRef.value?.validate(async (valid: boolean) => {
if (valid) {
loading.value = true;
const [err] = await to(register(registerForm.value));
if (!err) {
const username = registerForm.value.username;
await ElMessageBox.alert("<font color='red'>恭喜你,您的账号 " + username + ' 注册成功!</font>', '系统提示', {
dangerouslyUseHTMLString: true,
type: 'success'
});
await router.push('/login');
} else {
loading.value = false;
if (captchaEnabled.value) {
getCode();
}
}
}
});
};
/**
* 获取验证码
*/
@ -201,30 +336,274 @@ const doSocialLogin = (type: string) => {
});
};
onMounted(() => {
getCode();
initTenantList();
getLoginData();
const container = document.querySelector('.login-container');
const registerBtn = document.querySelector('.register-btn');
const loginBtn = document.querySelector('.login-btn');
registerBtn.addEventListener('click', () => {
container.classList.add('active');
});
loginBtn.addEventListener('click', () => {
container.classList.remove('active');
});
});
</script>
<style lang="scss" scoped>
.login {
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container-demo{
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background: linear-gradient(90deg,#e2e2e2,#c9d6ff);
}
.login-container{
position: relative;
width: 850px;
height: 550px;
background: #fff;
border-radius: 30px;
box-shadow: 0 0 30px rgba(0, 0, 0, .2);
margin: 20px;
overflow: hidden;
}
.form-box{
position: absolute;
right: 0;
width: 50%;
height: 100%;
background-image: url('../assets/images/login-background.jpg');
background-size: cover;
}
.title {
margin: 0px auto 30px auto;
background: #fff;
display: flex;
align-items: center;
color: #333;
text-align: center;
color: #707070;
padding: 10px;
z-index: 1;
transition: .6s ease-in-out 1.2s, visibility 0s 1s;
}
.login-container.active .form-box{
right: 50%;
}
.form-box.register{
visibility: hidden;
}
.login-container.active .form-box.register{
visibility: visible;
}
form{
width: 100%;
}
.login-container h1{
font-size: 36px;
margin: -10px 0;
}
.input-box {
position: relative;
margin: 20px 0;
}
.input-box input{
width: 100%;
padding: 13px 50px 13px 20px;
background: #eee;
border-radius: 8px;
border: none;
outline: none;
font-size: 16px;
color: #333;
font-weight: 500;
}
.input-box::placeholder{
color: #888;
font-weight: 400;
}
.input-box i{
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
font-size: 20px;
color: #888;
}
.forgot-link{
margin: -15px 0 15px;
}
.login-form {
.forgot-link a {
font-size: 14.5px;
color: #333;
text-decoration: none;
}
.btn {
width: 100%;
height: 48px;
background: #7494ec;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, .1);
border: none;
cursor: pointer;
font-size: 16px;
color: #fff;
font-weight: 600;
}
.login-container p {
font-size: 14.5px;
margin: 15px 0;
}
.social-icons {
display: flex;
justify-content: center;
}
.social-icons a{
display: inline-flex;
padding: 10px;
border: 2px solid #ccc;
border-radius: 8px;
font-size: 24px;
color: #333;
text-decoration: none;
margin: 0 8px;
}
.toggle-box{
position: absolute;
width: 100%;
height: 100%;
}
.toggle-box::before{
content: '';
position: absolute;
left: -250%;
width: 300%;
height: 100%;
background: #7494ec;
border-radius: 150px;
z-index: 2;
transition: 1.8s ease-in-out;
}
.login-container.active .toggle-box::before{
left: 50%;
}
.toggle-panel {
position: absolute;
width: 50%;
height: 100%;
/* background: seagreen; */
color: #fff;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
z-index: 2;
transition: .6s ease-in-out;
}
.toggle-panel.toggle-left{
left: 0;
transition-delay: 1.2s;
}
.login-container.active .toggle-panel.toggle-left{
left: -50%;
transition-delay: .6s;
}
.toggle-panel.toggle-right{
right: -50%;
transition-delay: .6s;
}
.login-container.active .toggle-panel.toggle-right{
right: 0;
transition-delay: 1.2s;
}
.toggle-panel p {
margin-bottom: 20px;
}
.toggle-panel .btn {
width: 160px;
height: 46px;
background: transparent;
border: 2px solid #fff;
box-shadow: none;
}
@media screen and (max-width: 650px) {
.login-container {
height: calc(100vh - 20px);
}
.form-box {
bottom: 0;
width: 100%;
height: 70%;
}
.login-container.active .form-box{
right: 0;
bottom: 30%;
}
.toggle-box::before {
left: 0;
top: -270%;
width: 100%;
height: 300%;
border-radius: 20vw;
}
.login-container.active .toggle-box::before{
left: 0;
top: 70%;
}
.toggle-panel {
width: 100%;
height: 30%;
}
.toggle-panel.toggle-left{
top: 0;
}
.login-container.active .toggle-panel.toggle-left{
left: 0;
top: -30%;
}
.toggle-panel.toggle-right{
right: 0;
bottom: -30%;
}
.login-container.active .toggle-panel.toggle-right{
bottom: 0;
}
}
@media screen and (max-width: 400px){
.form-box{
padding: 20px;
}
.toggle-panel h1 {
font-size: 30px;
}
}
.form-box {
border-radius: 6px;
background: #ffffff;
width: 400px;
@ -245,23 +624,30 @@ onMounted(() => {
}
}
.login-tip {
font-size: 13px;
text-align: center;
color: #bfbfbf;
}
.login-code {
width: 33%;
height: 40px;
float: right;
img {
height: 40px;
float: right;
cursor: pointer;
vertical-align: middle;
}
}
.register-code {
width: 33%;
height: 40px;
float: right;
img {
height: 40px;
float: right;
cursor: pointer;
vertical-align: middle;
}
}
.el-login-footer {
height: 40px;
line-height: 40px;
@ -275,8 +661,4 @@ onMounted(() => {
letter-spacing: 1px;
}
.login-code-img {
height: 40px;
padding-left: 12px;
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="register">
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
<h3 class="title">RuoYi-Vue-Plus多租户管理系统</h3>
<h3 class="title">Rime-Lin-Vue多租户管理系统</h3>
<el-form-item v-if="tenantEnabled" prop="tenantId">
<el-select v-model="registerForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
<el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option>
@ -50,7 +50,7 @@
</el-form>
<!-- 底部 -->
<div class="el-register-footer">
<span>Copyright © 2018-2024 疯狂的狮子Li All Rights Reserved.</span>
<span>Copyright © 2018-2024 Linsnaillo All Rights Reserved.</span>
</div>
</div>
</template>

View File

@ -55,9 +55,9 @@
<el-tab-pane label="修改密码" name="resetPwd">
<resetPwd />
</el-tab-pane>
<el-tab-pane label="第三方应用" name="thirdParty">
<!-- <el-tab-pane label="第三方应用" name="thirdParty">
<thirdParty :auths="state.auths" />
</el-tab-pane>
</el-tab-pane>-->
<el-tab-pane label="在线设备" name="onlineDevice">
<onlineDevice :devices="state.devices" />
</el-tab-pane>

View File

@ -0,0 +1,158 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div class="mb-[10px]">
<el-card shadow="hover">
<al-form ref="queryFormRef" :model="queryParams" :inline="true">
</al-form>
</el-card>
</div>
</transition>
</div>
<el-card shadow="hover">
<template #header>
<el-row :gutter="10" class="mb8">
<el-button></el-button>
</el-row>
</template>
</el-card>
</template>
<script setup name="Ollama" lang="ts">
import {OllamaForm, OllamaQuery, OllamaVO} from "@/api/test/ollama/types";
import {ref} from "vue";
import {addOllama, delOllama, getOllama, listOllama, updateOllama} from "@/api/test/ollama";
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const ollamaList = ref<OllamaVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(false);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const ollamaFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
const initFormData: OllamaForm = {
};
const data = reactive<PageData<OllamaForm, OllamaQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10
},
rules: {
}
});
const { queryParams, form, rules } = toRefs(data);
/**
* 查询列表
*/
const getList = async () => {
loading.value = true;
const res = await listOllama(queryParams.value);
ollamaList.value = res.rows;
total.valuw = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = { ...initFormData };
ollamaFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: OllamaVO[]) => {
ids.value = selection.map( (item) => item.id );
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = '新增'
}
/** 修改按钮操作 */
const handleUpdate = async (row?: OllamaVO) => {
reset();
const _id = row?.id || ids.value[0]
const res = await getOllama(_id);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = '修改'
}
/** 提交按钮 */
const submitForm = () => {
ollamaFormRef.value?.validate( async (valid: boolean) => {
if (valid){
buttonLoading.value = true;
if (form.value.id) {
await updateOllama(form.value).finally(() => buttonLoading.value = false);
}else {
await addOllama(form.value).finally( () => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess('修改成功');
dialog.visible = false;
await getList();
}
})
}
/** 删除按钮操作 */
const handleDelete = async (row?: OllamaVO) => {
const _ids = row?.id || ids.value;
await proxy?.$modal.confirm('是否确认删除配置编号为"' + _ids + '"的数据项?').finally(() => (loading.value = false));
await delOllama(_ids)
proxy?.$modal.msgSuccess('删除成功');
await getList();
}
const handleExport = () => {
proxy?.download(
'test/ollama/export',
{
...queryParams.value
},
`olllama_${new Date().getTime()}.xlsx`
)
}
onMounted(() => {
getList();
});
</script>