Quasar Sika Design Admin企業(yè)級(jí)中后臺(tái)管理系統(tǒng)解決方案
Quasar Sika Design Admin 是一個(gè)企業(yè)級(jí)中后臺(tái)前端 / 設(shè)計(jì)解決方案,中后臺(tái)管理模板,我們秉承 Ant Design 的設(shè)計(jì)價(jià)值觀(guān),致力于在設(shè)計(jì)規(guī)范和基礎(chǔ)組件的基礎(chǔ)上,繼續(xù)向上構(gòu)建,提煉出典型模板 / 業(yè)務(wù)組件 / 配套設(shè)計(jì)資源,進(jìn)一步提升企業(yè)級(jí)中后臺(tái)產(chǎn)品設(shè)計(jì)研發(fā)過(guò)程中的『用戶(hù)』和『設(shè)計(jì)者』的體驗(yàn)。
項(xiàng)目初衷
起于 Sika Design,不止于 Sika Design,每一個(gè)細(xì)節(jié)都是極致體驗(yàn) 愿景:開(kāi)源改變世界,Sika Design Admin 讓世界沒(méi)有難寫(xiě)的代碼。
項(xiàng)目特色
前端
- 優(yōu)雅美觀(guān):基于 Ant Design 體系精心設(shè)計(jì)
- 常見(jiàn)設(shè)計(jì)模式:開(kāi)源改變世界,Sika Design 讓世界沒(méi)有難寫(xiě)的代碼
- 最新技術(shù)棧:使用 Quasar&Vue&echarts 等前端前沿技術(shù)開(kāi)發(fā)
- 響應(yīng)式:針對(duì)不同屏幕大小設(shè)計(jì)
- 主題:可配置的主題滿(mǎn)足多樣化的品牌訴求
- 最佳實(shí)踐:良好的工程實(shí)踐助你持續(xù)產(chǎn)出高質(zhì)量代碼
后端
- 優(yōu)雅、簡(jiǎn)潔、規(guī)范而不失個(gè)性
- 抽象基礎(chǔ)組件
- 約束代碼規(guī)范
- 特色的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)實(shí)踐【上下文對(duì)象context+executor執(zhí)行者】
- 完整的代碼生成器-新模塊基礎(chǔ)功能零開(kāi)發(fā)
組織結(jié)構(gòu)
- quasar-sika-design-admin
- quasar-sika-design // 前端-基于vue+quasar構(gòu)建
- quasar-sika-design-server // 后端服務(wù)-基于springboot+mybatis
- doc // 文檔說(shuō)明
- sql // sql腳本
- quasar-sika-design-server-common // 基礎(chǔ)公共模塊;包括業(yè)務(wù)的constant+dto+query以及baseDTO+baseSrvice等等
- quasar-sika-design-server-core // 核心業(yè)務(wù)模塊;包括業(yè)務(wù)的service,核心領(lǐng)域?qū)崿F(xiàn)邏輯等等
- quasar-sika-design-server-ataaccess // 數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)層
- quasar-sika-design-server-generator // 代碼生成器模塊【無(wú)須開(kāi)發(fā)】
- quasar-sika-design-server-web // controller層
- sika-code-cor // 核心公共組件,包括且不限于緩存組件、代碼生成器組件、公共組件、數(shù)據(jù)訪(fǎng)問(wèn)、分布式鎖、腳手架規(guī)范
- cache // 緩存組件
- code-generator // 代碼生成器組件
- common // 公共組件
- databasse // 數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)組件
- hutool-starter // hutool基礎(chǔ)集成
- lock // 分布式組件
- standard-footer // 標(biāo)準(zhǔn)腳手架組件
技術(shù)選型
后端技術(shù)
前端技術(shù)
| 技術(shù) | 官網(wǎng) | 備注 |
|---|---|---|
| Vue | https://cn.vuejs.org/ | 漸進(jìn)式JavaScript 框架 |
| Quasar | http://www.quasarchs.com/ | 基于Vue實(shí)現(xiàn)的前端UI框架 |
| Echarts | https://echarts.apache.org/zh/index.html/ | 基于 JavaScript 的開(kāi)源可視化圖表庫(kù) |
| Lodashi | https://www.lodashjs.com/ | 一致性、模塊化、高性能的 JavaScript 實(shí)用工具庫(kù) |
Overview
基于 Quasar 實(shí)現(xiàn)的 Quasar Sika Design_Admin
PC端示例
mobile端示例
服務(wù)端代碼示例
Controller 由代碼生成器生成
package com.quasar.sika.design.server.business.menu.controller;
import java.util.List;
import com.sika.code.result.Result;
import com.sika.code.standard.base.controller.BaseStandardController;
import com.quasar.sika.design.server.business.menu.service.MenuService;
import com.quasar.sika.design.server.business.menu.pojo.dto.MenuDTO;
import com.quasar.sika.design.server.business.menu.pojo.query.MenuQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 菜單權(quán)限表 前端控制器
* </p>
*
* @author daiqi
* @since 2021-01-07 23:35:13
*/
@RestController(value = "menuController")
@RequestMapping("menu")
public class MenuController extends BaseStandardController {
@Autowired
private MenuService menuService;
@RequestMapping(value = "save")
public Result save(@RequestBody MenuDTO menuDto) {
return super.success(menuService.save(menuDto));
}
@RequestMapping(value = "save_batch")
public Result saveBatch(@RequestBody List<MenuDTO> menuDtos) {
return super.success(menuService.saveForBatch(menuDtos));
}
@RequestMapping(value = "update_by_id")
public Result updateById(@RequestBody MenuDTO menuDto) {
return super.success(menuService.updateById(menuDto));
}
@RequestMapping(value = "page")
public Result page(@RequestBody MenuQuery menuQuery) {
return super.success(menuService.page(menuQuery));
}
@RequestMapping(value = "find")
public Result find(@RequestBody MenuQuery menuQuery) {
return super.success(menuService.find(menuQuery));
}
@RequestMapping(value = "list")
public Result list(@RequestBody MenuQuery menuQuery) {
return super.success(menuService.list(menuQuery));
}
}
Service 由代碼生成器生成
package com.quasar.sika.design.server.business.menu.service;
import com.quasar.sika.design.server.business.menu.pojo.dto.MenuDTO;
import com.sika.code.standard.base.service.BaseStandardService;
import java.util.List;
/**
* <p>
* 菜單權(quán)限表 服務(wù)類(lèi)
* </p>
*
* @author daiqi
* @since 2021-01-07 23:35:09
*/
public interface MenuService extends BaseStandardService<MenuDTO> {
}
ServiceImpl 由代碼生成器生成
package com.quasar.sika.design.server.business.menu.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Lists;
import com.quasar.sika.design.server.business.menu.convert.MenuConvert;
import com.quasar.sika.design.server.business.menu.entity.MenuEntity;
import com.quasar.sika.design.server.business.menu.mapper.MenuMapper;
import com.quasar.sika.design.server.business.menu.pojo.dto.MenuDTO;
import com.quasar.sika.design.server.business.menu.pojo.query.MenuQuery;
import com.quasar.sika.design.server.business.menu.service.MenuService;
import com.quasar.sika.design.server.business.rolemenu.service.RoleMenuService;
import com.sika.code.standard.base.convert.BaseConvert;
import com.sika.code.standard.base.service.impl.BaseStandardServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
/**
* <p>
* 菜單權(quán)限表 服務(wù)實(shí)現(xiàn)類(lèi)
* </p>
*
* @author daiqi
* @since 2021-01-07 23:35:10
*/
@Service(value = "menuService")
public class MenuServiceImpl extends BaseStandardServiceImpl<MenuMapper, MenuEntity, MenuDTO> implements MenuService {
@Autowired
private MenuMapper menuMapper;
@Override
protected BaseConvert<MenuEntity, MenuDTO> convert() {
return MenuConvert.INSTANCE;
}
}
Mapper 由代碼生成器生成
package com.quasar.sika.design.server.business.menu.mapper;
import com.quasar.sika.design.server.business.menu.entity.MenuEntity;
import org.springframework.stereotype.Repository;
import com.sika.code.standard.base.basemapper.BaseStandardMapper;
/**
* <p>
* 菜單權(quán)限表 Mapper 接口
* </p>
*
* @author daiqi
* @since 2021-01-07 23:35:08
*/
@Repository
public interface MenuMapper extends BaseStandardMapper<MenuEntity> {
}
Xml 由代碼生成器生成
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.quasar.sika.design.server.business.menu.mapper.MenuMapper">
<!-- 通用查詢(xún)映射結(jié)果 -->
<resultMap id="ResultMap" type="com.quasar.sika.design.server.business.menu.entity.MenuEntity">
<result column="id" property="id" />
<result column="create_by" property="createBy" />
<result column="update_by" property="updateBy" />
<result column="create_date" property="createDate" />
<result column="update_date" property="updateDate" />
<result column="version" property="version" />
<result column="available" property="available" />
<result column="is_deleted" property="isDeleted" />
<result column="remark" property="remark" />
<result column="menu_name" property="menuName" />
<result column="parent_id" property="parentId" />
<result column="order_num" property="orderNum" />
<result column="url" property="url" />
<result column="target" property="target" />
<result column="menu_type" property="menuType" />
<result column="visible" property="visible" />
<result column="perms" property="perms" />
<result column="icon" property="icon" />
</resultMap>
<!-- 通用查詢(xún)結(jié)果列 -->
<sql id="columnList">
id, create_by, update_by, create_date, update_date, version, available, is_deleted, remark, menu_name, parent_id, order_num, url, target, menu_type, visible, perms, icon
</sql>
<!-- 根據(jù)查詢(xún)條件獲取列表信息 -->
<select id="listByQuery" resultMap="ResultMap" parameterType="com.quasar.sika.design.server.business.menu.pojo.query.MenuQuery" >
SELECT <include refid="columnList" />
FROM sika_menu
<where>
is_deleted = 0
<include refid="query_sql" />
</where>
</select>
<!-- 根據(jù)查詢(xún)條件獲取Id列表信息 -->
<select id="listIdByQuery" resultType="java.lang.Long" parameterType="com.quasar.sika.design.server.business.menu.pojo.query.MenuQuery" >
SELECT id
FROM sika_menu
<where>
is_deleted = 0
<include refid="query_sql" />
</where>
</select>
<!-- 根據(jù)查詢(xún)條件獲取實(shí)體信息 -->
<select id="findByQuery" resultMap="ResultMap" parameterType="com.quasar.sika.design.server.business.menu.pojo.query.MenuQuery" >
SELECT <include refid="columnList" />
FROM sika_menu
<where>
is_deleted = 0
<include refid="query_sql" />
</where>
LIMIT 1
</select>
<!-- 根據(jù)查詢(xún)條件獲取表id -->
<select id="findIdByQuery" resultType="java.lang.Long" parameterType="com.quasar.sika.design.server.business.menu.pojo.query.MenuQuery" >
SELECT id
FROM sika_menu
<where>
is_deleted = 0
<include refid="query_sql" />
</where>
LIMIT 1
</select>
<!-- 根據(jù)查詢(xún)條件獲取分頁(yè)信息 -->
<select id="pageByQuery" resultMap="ResultMap" parameterType="com.quasar.sika.design.server.business.menu.pojo.query.MenuQuery" >
SELECT <include refid="columnList" />
FROM sika_menu
<where>
is_deleted = 0
<include refid="query_sql" />
</where>
<include refid="order_by_sql"/>
</select>
<!-- 根據(jù)查詢(xún)條件獲取總數(shù)量信息 -->
<select id="totalCountByQuery" resultType="Integer" parameterType="com.quasar.sika.design.server.business.menu.pojo.query.MenuQuery" >
SELECT count(*)
FROM sika_menu
<where>
is_deleted = 0
<include refid="query_sql" />
</where>
</select>
<!-- 根據(jù)查詢(xún)條件SQL -->
<sql id="query_sql" >
<if test="query.id != null">AND id = #{query.id}</if>
<if test="query.menuId != null">AND id = #{query.menuId}</if>
<if test="query.menuName != null">AND menu_name = #{query.menuName}</if>
<if test="query.parentId != null">AND parent_id = #{query.parentId}</if>
<if test="query.orderNum != null">AND order_num = #{query.orderNum}</if>
<if test="query.url != null">AND url = #{query.url}</if>
<if test="query.target != null">AND target = #{query.target}</if>
<if test="query.menuType != null">AND menu_type = #{query.menuType}</if>
<if test="query.visible != null">AND visible = #{query.visible}</if>
<if test="query.perms != null">AND perms = #{query.perms}</if>
<if test="query.icon != null">AND icon = #{query.icon}</if>
<if test="query.ids != null and query.ids.size() > 0">
AND id in
<foreach item="item" collection="query.ids" separator="," open="(" close=")" index="">
#{item}
</foreach>
</if>
</sql>
<!-- 排序的sql -->
<sql id="order_by_sql">
<if test="query.sortColumn != null and query.sortType != null" >
ORDER BY
<include refid="order_by_column_sql"/>
<include refid="order_by_type_sql"/>
</if>
</sql>
<!-- 排序列名的sql -->
<sql id="order_by_column_sql">
<choose>
<when test="query.sortColumn == 'menuId'">
id
</when>
<otherwise>
id
</otherwise>
</choose>
</sql>
<!-- 排序類(lèi)型的sql -->
<sql id="order_by_type_sql">
<choose>
<when test="query.sortType == 'DESC'">
DESC
</when>
<otherwise>
ASC
</otherwise>
</choose>
</sql>
</mapper>
Context【領(lǐng)域上下文對(duì)象】
package com.quasar.sika.design.server.common.auth.context;
import com.quasar.sika.design.server.common.auth.executor.AuthRegisterRequestExecutor;
import com.quasar.sika.design.server.common.auth.pojo.request.AuthRegisterRequest;
import com.quasar.sika.design.server.common.captcha.pojo.request.CaptchaCheckRequest;
import com.quasar.sika.design.server.common.mail.context.CheckMailCodeContext;
import com.quasar.sika.design.server.common.mail.pojo.request.CheckMailRequest;
import com.quasar.sika.design.server.common.shiro.util.SHA256Util;
import com.sika.code.standard.base.pojo.context.BaseStandardContext;
import com.sika.code.standard.base.pojo.executor.BaseStandardExecutor;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author daiqi
* @create 2021-01-09 18:02
*/
@Data
@Accessors(chain = true)
public class AuthRegisterContext extends BaseStandardContext {
private AuthRegisterRequest registerRequest;
private CheckMailRequest checkMailRequest;
protected CaptchaCheckRequest captchaCheckRequest;
private CheckMailCodeContext checkMailCodeContext;
private Boolean bindOauthUser;
@Override
public void initCustomer() {
checkMailCodeContext = new CheckMailCodeContext().setRequest(checkMailRequest);
registerRequest.setPassword(SHA256Util.sha256(registerRequest));
}
@Override
protected Class<? extends BaseStandardExecutor> buildExecutorClass() {
return AuthRegisterRequestExecutor.class;
}
}
Executor領(lǐng)域執(zhí)行者對(duì)象【按領(lǐng)域劃分】
package com.quasar.sika.design.server.common.auth.executor;
import cn.hutool.core.util.BooleanUtil;
import com.quasar.sika.design.server.common.auth.context.AuthRegisterContext;
import com.quasar.sika.design.server.common.auth.domain.AuthDomain;
import com.quasar.sika.design.server.common.auth.pojo.request.AuthLoginRequest;
import com.quasar.sika.design.server.common.auth.pojo.request.AuthRegisterRequest;
import com.quasar.sika.design.server.common.auth.pojo.response.AuthResponse;
import com.sika.code.basic.pojo.dto.ServiceResult;
import com.sika.code.exception.BusinessException;
import com.sika.code.standard.base.pojo.executor.BaseStandardExecutor;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author daiqi
* @create 2021-01-09 18:02
*/
@Data
@Accessors(chain = true)
public class AuthRegisterRequestExecutor extends BaseStandardExecutor<AuthRegisterContext> implements AuthDomain {
@Override
protected void executeBefore() {
verify();
}
protected void verify() {
// 圖片驗(yàn)證碼校驗(yàn)
captchaService().checkCaptchaVerifyCode(context.getCaptchaCheckRequest());
// 郵箱驗(yàn)證碼校驗(yàn)
executorManager().execute(context.getCheckMailCodeContext());
// 校驗(yàn)用戶(hù)名
AuthRegisterRequest registerRequest = context.getRegisterRequest();
authService().checkRegisterUsername(registerRequest);
// 校驗(yàn)郵箱
authService().checkRegisterEmail(registerRequest);
// 校驗(yàn)手機(jī)號(hào)
authService().checkRegisterPhone(registerRequest);
}
@Override
protected ServiceResult doExecute() {
AuthRegisterRequest registerRequest = context.getRegisterRequest();
boolean saveSuccess = userService().save(registerRequest);
if (BooleanUtil.isFalse(saveSuccess)) {
throw new BusinessException("保存失敗,請(qǐng)校驗(yàn)注冊(cè)參數(shù)");
}
return ServiceResult.newInstanceOfSucResult(AuthResponse.success(registerRequest));
}
@Override
protected void executeAfter() {
// 自動(dòng)登錄
AuthRegisterRequest registerRequest = context.getRegisterRequest();
AuthLoginRequest request = new AuthLoginRequest(registerRequest.getUsername(), registerRequest.getPassword());
request.setEncryptedPassword(true);
if (BooleanUtil.isTrue(context.getBindOauthUser())) {
authService().bindOauthUser(request);
} else {
authService().login(request);
}
// 移除緩存
captchaService().removeCaptchaVerifyCode(context.getCaptchaCheckRequest());
mailService().removeMailCode(context.getCheckMailRequest());
}
}
前端環(huán)境和依賴(lài)
- node
- yarn
- webpack
- eslint
- @vue/cli ~3
- Quasar - Quasar Of Vue 實(shí)現(xiàn)
請(qǐng)注意,我們強(qiáng)烈建議本項(xiàng)目使用 Yarn 包管理工具,這樣可以與本項(xiàng)目演示站所加載完全相同的依賴(lài)版本 (yarn.lock) 。由于我們沒(méi)有對(duì)依賴(lài)進(jìn)行強(qiáng)制的版本控制,采用非 yarn 包管理進(jìn)行引入時(shí),可能由于 Pro 所依賴(lài)的庫(kù)已經(jīng)升級(jí)版本而引入了新版本所導(dǎo)致的問(wèn)題。作者可能會(huì)由于時(shí)間問(wèn)題無(wú)法及時(shí)排查而導(dǎo)致您采用本項(xiàng)目作為基項(xiàng)目而出現(xiàn)問(wèn)題。
項(xiàng)目下載和運(yùn)行
- 拉取項(xiàng)目代碼
git clone https://github.com/dq-open-cloud/quasar-sika-design.git
cd quasar-sika-design
- 安裝依賴(lài)
yarn install
- 開(kāi)發(fā)模式運(yùn)行
quasar dev
- 編譯項(xiàng)目
quasar build
- Lints and fixes files
yarn run lint
文檔待完善
啟動(dòng)步驟
- 找到QuasarSikaDesignServerApplication直接運(yùn)行
- 前端quasar-dev啟動(dòng)運(yùn)行即可
其他說(shuō)明
-
關(guān)于 Issue 反饋 (重要!重要!重要!) 請(qǐng)?jiān)陂_(kāi) Issue 前,先閱讀該內(nèi)容:Issue / PR 編寫(xiě)建議
-
項(xiàng)目使用的 quasar-cli, 請(qǐng)確保你所使用的 quasar-cli 是新版,并且已經(jīng)學(xué)習(xí) cli 官方文檔使用教程
-
關(guān)閉 Eslint (不推薦) 移除
package.json中eslintConfig整個(gè)節(jié)點(diǎn)代碼,vue.config.js下的lintOnSave值改為false -
用于生產(chǎn)環(huán)境,請(qǐng)使用
release版本代碼,使用 master 代碼出現(xiàn)的任何問(wèn)題需要你自行解決 -
后端提供的
Mysql和Redis環(huán)境屬于線(xiàn)上測(cè)試環(huán)境,內(nèi)測(cè)階段請(qǐng)大家一定不要隨意增刪字段
瀏覽器兼容
- Chrome for Android >= 87
- Firefox for Android >= 83
- Android >= 81
- Chrome >= 77
- Edge >= 84
- Firefox >= 74
- iOS >= 10.3
- Opera >= 68
- Safari >= 11
| |
|
|
|
|
|---|---|---|---|---|
| IE10, Edge | last 2 versions | last 2 versions | last 2 versions | last 2 versions |
Contributors
This project exists thanks to all the people who contribute.
