diff --git a/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/CacheConstants.java b/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/CacheConstants.java
new file mode 100644
index 0000000..7ee9b7e
--- /dev/null
+++ b/jing-common/jing-common-core/src/main/java/com/jing/common/core/constant/CacheConstants.java
@@ -0,0 +1,59 @@
+package com.jing.common.core.constant;
+
+/**
+ * 缓存常量信息
+ *
+ * @author ruoyi
+ */
+public class CacheConstants
+{
+ /**
+ * 缓存有效期,默认720(分钟)
+ */
+ public final static long EXPIRATION = 720;
+
+ /**
+ * 缓存刷新时间,默认120(分钟)
+ */
+ public final static long REFRESH_TIME = 120;
+
+ /**
+ * 密码最大错误次数
+ */
+ public final static int PASSWORD_MAX_RETRY_COUNT = 5;
+
+ /**
+ * 密码锁定时间,默认10(分钟)
+ */
+ public final static long PASSWORD_LOCK_TIME = 10;
+
+ /**
+ * 权限缓存前缀
+ */
+ public final static String LOGIN_TOKEN_KEY = "login_tokens:";
+
+ /**
+ * 验证码 redis key
+ */
+ public static final String CAPTCHA_CODE_KEY = "captcha_codes:";
+
+ /**
+ * 参数管理 cache key
+ */
+ public static final String SYS_CONFIG_KEY = "sys_config:";
+
+ /**
+ * 字典管理 cache key
+ */
+ public static final String SYS_DICT_KEY = "sys_dict:";
+
+ /**
+ * 登录账户密码错误次数 redis key
+ */
+ public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:";
+
+ /**
+ * 登录IP黑名单 cache key
+ */
+ public static final String SYS_LOGIN_BLACKIPLIST = SYS_CONFIG_KEY + "sys.login.blackIPList";
+}
diff --git a/jing-common/jing-common-core/src/main/java/com/jing/common/core/exception/CaptchaException.java b/jing-common/jing-common-core/src/main/java/com/jing/common/core/exception/CaptchaException.java
new file mode 100644
index 0000000..996b3d1
--- /dev/null
+++ b/jing-common/jing-common-core/src/main/java/com/jing/common/core/exception/CaptchaException.java
@@ -0,0 +1,16 @@
+package com.jing.common.core.exception;
+
+/**
+ * 验证码错误异常类
+ *
+ * @author ruoyi
+ */
+public class CaptchaException extends RuntimeException
+{
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/jing-common/jing-common-core/src/main/java/com/jing/common/core/exception/user/CaptchaExpireException.java b/jing-common/jing-common-core/src/main/java/com/jing/common/core/exception/user/CaptchaExpireException.java
new file mode 100644
index 0000000..50c4f43
--- /dev/null
+++ b/jing-common/jing-common-core/src/main/java/com/jing/common/core/exception/user/CaptchaExpireException.java
@@ -0,0 +1,16 @@
+package com.jing.common.core.exception.user;
+
+/**
+ * 验证码失效异常类
+ *
+ * @author ruoyi
+ */
+public class CaptchaExpireException extends UserException
+{
+ private static final long serialVersionUID = 1L;
+
+ public CaptchaExpireException()
+ {
+ super("user.jcaptcha.expire", null);
+ }
+}
diff --git a/jing-common/jing-common-core/src/main/java/com/jing/common/core/text/CharsetKit.java b/jing-common/jing-common-core/src/main/java/com/jing/common/core/text/CharsetKit.java
new file mode 100644
index 0000000..e7761aa
--- /dev/null
+++ b/jing-common/jing-common-core/src/main/java/com/jing/common/core/text/CharsetKit.java
@@ -0,0 +1,86 @@
+package com.jing.common.core.text;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import com.jing.common.core.utils.StringUtils;
+
+/**
+ * 字符集工具类
+ *
+ * @author ruoyi
+ */
+public class CharsetKit
+{
+ /** ISO-8859-1 */
+ public static final String ISO_8859_1 = "ISO-8859-1";
+ /** UTF-8 */
+ public static final String UTF_8 = "UTF-8";
+ /** GBK */
+ public static final String GBK = "GBK";
+
+ /** ISO-8859-1 */
+ public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1);
+ /** UTF-8 */
+ public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8);
+ /** GBK */
+ public static final Charset CHARSET_GBK = Charset.forName(GBK);
+
+ /**
+ * 转换为Charset对象
+ *
+ * @param charset 字符集,为空则返回默认字符集
+ * @return Charset
+ */
+ public static Charset charset(String charset)
+ {
+ return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset);
+ }
+
+ /**
+ * 转换字符串的字符集编码
+ *
+ * @param source 字符串
+ * @param srcCharset 源字符集,默认ISO-8859-1
+ * @param destCharset 目标字符集,默认UTF-8
+ * @return 转换后的字符集
+ */
+ public static String convert(String source, String srcCharset, String destCharset)
+ {
+ return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset));
+ }
+
+ /**
+ * 转换字符串的字符集编码
+ *
+ * @param source 字符串
+ * @param srcCharset 源字符集,默认ISO-8859-1
+ * @param destCharset 目标字符集,默认UTF-8
+ * @return 转换后的字符集
+ */
+ public static String convert(String source, Charset srcCharset, Charset destCharset)
+ {
+ if (null == srcCharset)
+ {
+ srcCharset = StandardCharsets.ISO_8859_1;
+ }
+
+ if (null == destCharset)
+ {
+ destCharset = StandardCharsets.UTF_8;
+ }
+
+ if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset))
+ {
+ return source;
+ }
+ return new String(source.getBytes(srcCharset), destCharset);
+ }
+
+ /**
+ * @return 系统字符集编码
+ */
+ public static String systemCharset()
+ {
+ return Charset.defaultCharset().name();
+ }
+}
diff --git a/jing-ui/src/api/product/category.js b/jing-ui/src/api/product/category.js
new file mode 100644
index 0000000..ea8aadb
--- /dev/null
+++ b/jing-ui/src/api/product/category.js
@@ -0,0 +1,10 @@
+import request from '@/utils/request'
+
+// 查询商品信息
+export function listCategory(query) {
+ return request({
+ url: '/product/category/list',
+ method: 'get',
+ params: query
+ })
+}
diff --git a/jing-ui/src/assets/icons/svg/cascader.svg b/jing-ui/src/assets/icons/svg/cascader.svg
new file mode 100644
index 0000000..e256024
--- /dev/null
+++ b/jing-ui/src/assets/icons/svg/cascader.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/jing-ui/src/assets/icons/svg/chart.svg b/jing-ui/src/assets/icons/svg/chart.svg
new file mode 100644
index 0000000..27728fb
--- /dev/null
+++ b/jing-ui/src/assets/icons/svg/chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/jing-ui/src/plugins/cache.js b/jing-ui/src/plugins/cache.js
new file mode 100644
index 0000000..6b5c00b
--- /dev/null
+++ b/jing-ui/src/plugins/cache.js
@@ -0,0 +1,77 @@
+const sessionCache = {
+ set (key, value) {
+ if (!sessionStorage) {
+ return
+ }
+ if (key != null && value != null) {
+ sessionStorage.setItem(key, value)
+ }
+ },
+ get (key) {
+ if (!sessionStorage) {
+ return null
+ }
+ if (key == null) {
+ return null
+ }
+ return sessionStorage.getItem(key)
+ },
+ setJSON (key, jsonValue) {
+ if (jsonValue != null) {
+ this.set(key, JSON.stringify(jsonValue))
+ }
+ },
+ getJSON (key) {
+ const value = this.get(key)
+ if (value != null) {
+ return JSON.parse(value)
+ }
+ },
+ remove (key) {
+ sessionStorage.removeItem(key);
+ }
+}
+const localCache = {
+ set (key, value) {
+ if (!localStorage) {
+ return
+ }
+ if (key != null && value != null) {
+ localStorage.setItem(key, value)
+ }
+ },
+ get (key) {
+ if (!localStorage) {
+ return null
+ }
+ if (key == null) {
+ return null
+ }
+ return localStorage.getItem(key)
+ },
+ setJSON (key, jsonValue) {
+ if (jsonValue != null) {
+ this.set(key, JSON.stringify(jsonValue))
+ }
+ },
+ getJSON (key) {
+ const value = this.get(key)
+ if (value != null) {
+ return JSON.parse(value)
+ }
+ },
+ remove (key) {
+ localStorage.removeItem(key);
+ }
+}
+
+export default {
+ /**
+ * 会话级缓存
+ */
+ session: sessionCache,
+ /**
+ * 本地缓存
+ */
+ local: localCache
+}