1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee

Merge branch 'main' into production

This commit is contained in:
程序员小墨 2023-04-25 02:21:39 +08:00
commit eae5ac5310
90 changed files with 1735 additions and 541 deletions

View File

@ -214,9 +214,7 @@ cd ../
# ############ # ############
# 进入 frontend 目录 # 进入 frontend 目录
cd frontend cd frontend
# 建议使用 cnpm 安装依赖,如果没有安装 cnpm可以通过 npm i cnpm 进行安装 npm install
cnpm install
# npm install --legacy-peer-deps
# 回到项目根目录下 # 回到项目根目录下
cd ../ cd ../
``` ```
@ -306,6 +304,17 @@ npm run package
#### 管理员前端项目打包并嵌入后端
```
cd frontend
npm run build
```
然后将 `frontend/dist` 文件夹移动到 `backend/microservice-gateway/src/main/resources/static` 文件夹下,并修改文件夹名称 `dist``manage`
#### 后端配置 #### 后端配置
##### 配置业务域名 ##### 配置业务域名
@ -314,6 +323,9 @@ npm run package
``` ```
const baseUrl = "https://【⚠此处修改为你的业务域名】/" // 以 / 结尾例如https://epp.only4.work/ const baseUrl = "https://【⚠此处修改为你的业务域名】/" // 以 / 结尾例如https://epp.only4.work/
// 或者也可使用相对路径,例如:
const baseUrl = "/"
``` ```
@ -321,7 +333,7 @@ const baseUrl = "https://【⚠此处修改为你的业务域名】/" // 以 /
修改 `backend/microservice-provider-access-8002/src/main/resources/static/access/assets/js/websocket.js` 文件 修改 `backend/microservice-provider-access-8002/src/main/resources/static/access/assets/js/websocket.js` 文件
``` ```
window.wsUrl = 'ws://【⚠此处修改为你的业务域名】/access/websocket/1'; window.wsUrl = 'ws://【⚠此处修改为你的业务域名】/access/websocket/';
``` ```
> 注意,如果使用了 SSL 证书,那么 ws:// 要换成 wss:// > 注意,如果使用了 SSL 证书,那么 ws:// 要换成 wss://
@ -350,6 +362,22 @@ const envVersion = "【⚠此处修改为当前小程序环境】" // 正式版
##### 配置 Gateway 限流策略(可选)
> 如果你不懂这是在做什么,请直接跳过这一步
修改 `backend/microservice-gateway/src/main/resources/application.yml` 文件中 `routes` 中各个微服务的 `filters` 例如:
```yml
filters: # 路由过滤器,使用自定义的限流过滤器工厂
- name: RateLimitByIp # 设置每秒允许5个请求每次请求需要1个令牌
args:
rate: 5.0
permits: 1
```
##### 打 jar 包 ##### 打 jar 包
IDEA 中右侧 Maven 双击 Lifestyle 的 package打包完成后的 jar 包可在以下位置找到 IDEA 中右侧 Maven 双击 Lifestyle 的 package打包完成后的 jar 包可在以下位置找到
@ -389,6 +417,7 @@ IDEA 中右侧 Maven 双击 Lifestyle 的 package打包完成后的 jar 包
🌟若使用的 Nacos 版本大于或等于 Nacos 2.2.0.1,则需要配置自定义密钥: 🌟若使用的 Nacos 版本大于或等于 Nacos 2.2.0.1,则需要配置自定义密钥:
```properties ```properties
# 密钥值可以自己指定,此处的自定义密钥来自 nacos 官网
nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg= nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg=
``` ```
@ -396,7 +425,9 @@ nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMT
#### nginx 代理配置(可选) #### nginx 代理配置
##### 反向代理(可选)
配置文件在 `nginx-conf` 目录下(不能直接拿来用,需要根据自己的实际情况来改) 配置文件在 `nginx-conf` 目录下(不能直接拿来用,需要根据自己的实际情况来改)
@ -409,6 +440,13 @@ server
listen 80; listen 80;
listen 443 ssl http2; listen 443 ssl http2;
# 并发限制 限制当前站点最大并发数
limit_conn perserver 50;
# 单IP限制 限制单个IP访问最大并发数
limit_conn perip 10;
# 流量限制 限制每个请求的流量上限单位KB
limit_rate 8192k;
# 核心配置 # 核心配置
location / { location / {
proxy_pass http://127.0.0.1:5203; # ⚠ Gateway 微服务项目本地运行的端口 proxy_pass http://127.0.0.1:5203; # ⚠ Gateway 微服务项目本地运行的端口
@ -425,6 +463,10 @@ server
} }
``` ```
##### SSL证书配置可选
如果域名配置为 https:// 则需要配置 SSL 证书。SSL证书可在 nginx 中进行配置,具体配置方法此处省略。
## 启动项目 ## 启动项目

View File

@ -42,6 +42,28 @@
Java代码中小程序AppID、密钥处理小程序代码中小程序AppID处理 Java代码中小程序AppID、密钥处理小程序代码中小程序AppID处理
# TODO 全部域名配置的地方添加 FIXME 环境配置
# VSCode 全局搜索排除
node_modules,./postman-collection,target,out,.idea,@deprecated
# host 文件地址
C:\Windows\System32\drivers\etc
配置项
127.0.0.1 epp.only4.work
127.0.0.1 epp-prod.only4.work
查看配置
ping epp.only4.work
ping epp-prod.only4.work
# 后端项目启动命令 # 后端项目启动命令

View File

@ -4,7 +4,7 @@ import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
public enum OrderStatus { public enum OrderStatus {
PENDING("Pending", "等待确认"), PENDING("Pending", "等待支付"),
PROCESSING("Processing", "已支付,等待发货中"), PROCESSING("Processing", "已支付,等待发货中"),
SHIPPED("Shipped", "已发货,等待确认收货"), SHIPPED("Shipped", "已发货,等待确认收货"),
DELIVERED("Delivered", "已送达"), DELIVERED("Delivered", "已送达"),

View File

@ -4,10 +4,12 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@Accessors(chain = true) // 链式写法 @Accessors(chain = true) // 链式写法
public class Good { public class Good implements Serializable {
Long id; Long id;
String goodsName; String goodsName;
Integer categoryId; Integer categoryId;

View File

@ -4,10 +4,12 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@Accessors(chain = true) // 链式写法 @Accessors(chain = true) // 链式写法
public class GoodCategory { public class GoodCategory implements Serializable {
Long id; Long id;
String categoryName; String categoryName;
Integer order; Integer order;

View File

@ -4,13 +4,14 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@Accessors(chain = true) // 链式写法 @Accessors(chain = true) // 链式写法
public class Order { public class Order implements Serializable {
private Long id; private Long id;
private Integer userId; private Integer userId;
private LocalDateTime orderDate; private LocalDateTime orderDate;

View File

@ -4,10 +4,12 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.io.Serializable;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@Accessors(chain = true) // 链式写法 @Accessors(chain = true) // 链式写法
public class OrderDetail { public class OrderDetail implements Serializable {
private Long id; private Long id;
private Long orderId; private Long orderId;
private Long goodId; private Long goodId;

View File

@ -0,0 +1,18 @@
package com.cxyxiaomo.epp.common.query;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
@Data
@NoArgsConstructor
@Accessors(chain = true) // 链式写法
// 微服务必须要实现Serializable
public class OrderQuery implements Serializable {
private Long id;
private Integer userId;
private String orderStatusCode;
private String expressId;
}

View File

@ -9,7 +9,6 @@ import org.springframework.beans.BeanUtils;
import java.io.Serializable; import java.io.Serializable;
import java.math.RoundingMode; import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -22,10 +21,10 @@ import java.util.stream.Collectors;
public class OrderVO implements Serializable { public class OrderVO implements Serializable {
private String id; private String id;
private Integer userId; private Integer userId;
private LocalDateTime orderDate;
private String orderStatus; private String orderStatus;
private String orderStatusCode; private String orderStatusCode;
private String orderPrice; private String orderPrice;
private String orderDate;
private String payDate; private String payDate;
private String cancelDate; private String cancelDate;
private String shipDate; private String shipDate;
@ -47,6 +46,9 @@ public class OrderVO implements Serializable {
String price = order.getOrderPrice().setScale(2, RoundingMode.FLOOR).toPlainString(); String price = order.getOrderPrice().setScale(2, RoundingMode.FLOOR).toPlainString();
orderVO.setOrderPrice(price); orderVO.setOrderPrice(price);
if (order.getOrderDate() != null) {
orderVO.setOrderDate(order.getOrderDate().toString().replace("T", " "));
}
if (order.getPayDate() != null) { if (order.getPayDate() != null) {
orderVO.setPayDate(order.getPayDate().toString().replace("T", " ")); orderVO.setPayDate(order.getPayDate().toString().replace("T", " "));
} }

View File

@ -0,0 +1,79 @@
package com.cxyxiaomo.epp.gateway.Factory;
import com.google.common.util.concurrent.RateLimiter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
// 一个自定义的限流过滤器工厂
public class RateLimitByIpGatewayFilterFactory extends AbstractGatewayFilterFactory<RateLimitByIpGatewayFilterFactory.Config> {
// 用于存储IP地址和对应的计数器
private static final Map<String, RateLimiter> RATE_LIMITER_CACHE = new ConcurrentHashMap<>();
public RateLimitByIpGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
// 获取请求的IP地址
ServerHttpRequest request = exchange.getRequest();
String ip = request.getRemoteAddress().getAddress().getHostAddress();
// 根据IP地址获取对应的限流器
RateLimiter rateLimiter = RATE_LIMITER_CACHE.get(ip);
if (rateLimiter == null) {
// 如果不存在则创建一个新的限流器并放入缓存中
rateLimiter = RateLimiter.create(config.getRate());
RATE_LIMITER_CACHE.put(ip, rateLimiter);
}
// 判断请求是否被限流
if (rateLimiter.tryAcquire(config.getPermits())) {
// 如果没有被限流则放行
return chain.filter(exchange);
} else {
System.out.println("限流ip: " + ip);
// 如果被限流则返回429状态码Too Many Requests
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
return response.setComplete();
}
};
}
// 配置类用于接收配置参数
public static class Config {
// 每秒允许的请求数
private double rate;
// 每次请求需要的令牌数
private int permits;
public double getRate() {
return rate;
}
public void setRate(double rate) {
this.rate = rate;
}
public int getPermits() {
return permits;
}
public void setPermits(int permits) {
this.permits = permits;
}
}
}

View File

@ -40,20 +40,38 @@ spring:
predicates: predicates:
- Path=/user/** - Path=/user/**
- Method=GET,POST - Method=GET,POST
filters: # 路由过滤器,使用自定义的限流过滤器工厂
- name: RateLimitByIp # 设置每秒允许5个请求每次请求需要1个令牌
args:
rate: 10.0
permits: 1
- id: access - id: access
uri: lb://microservice-provider-access uri: lb://microservice-provider-access
predicates: predicates:
- Path=/access/** - Path=/access/**
- Method=GET,POST - Method=GET,POST
filters: # 路由过滤器,使用自定义的限流过滤器工厂
- name: RateLimitByIp # 设置每秒允许5个请求每次请求需要1个令牌
args:
rate: 10.0
permits: 1
- id: access-websocket - id: access-websocket
uri: lb:ws://microservice-provider-access uri: lb:ws://microservice-provider-access
predicates: predicates:
- Path=/access/websocket/** - Path=/access/websocket/**
- id: shop - id: shop
uri: lb://microservice-provider-shop uri: lb://microservice-provider-shop
predicates: predicates:
- Path=/shop/** - Path=/shop/**
- Method=GET,POST - Method=GET,POST
filters: # 路由过滤器,使用自定义的限流过滤器工厂
- name: RateLimitByIp # 设置每秒允许5个请求每次请求需要1个令牌
args:
rate: 10.0
permits: 1
- id: test1 - id: test1
uri: lb://microservice-provider-test uri: lb://microservice-provider-test

View File

@ -1 +1,3 @@
编辑后右键 Compile And Reload File 修改即可生效,不用频繁重启项目 编辑后右键 Compile And Reload File 修改即可生效,不用频繁重启项目
manage 文件夹下为 frontend 项目打包产物

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -0,0 +1,20 @@
.scan-result-container {
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
z-index: 20;
display: grid;
place-items: center;
}
.scan-result-image {
width: min(55vw, 55vh);
transition: 0.3s;
transform: scale(0.001);
}
.scan-result-image-show {
transform: scale(1);
}

View File

@ -62,7 +62,7 @@
} }
.hidden { .hidden {
display: none; display: none !important;
} }
/* 弹窗列表样式 */ /* 弹窗列表样式 */
@ -82,6 +82,7 @@
box-sizing: border-box; box-sizing: border-box;
place-items: center; place-items: center;
background-color: #f6f6f6; background-color: #f6f6f6;
cursor: pointer;
} }
.gate-list-item > p { .gate-list-item > p {

View File

@ -0,0 +1,39 @@
const URLScanSuccessImage = "./assets/svg/scan-success.svg"
const URLEnterGateImage = "./assets/svg/enter-gate.svg"
const DOMFullScreenMask = document.getElementById("full-screen-mask")
const DOMScanResultContainer = document.getElementById("scan-result-container")
const DOMQRCodeScanSuccessImage = document.getElementById("qrcode-scan-success")
var isShowing = false
function showResult(imageType) {
console.log("showResult")
if (isShowing) {
// console.log("showResult() skipped coz showing.")
// 等待 0.1s 重试
setTimeout(() => showResult(imageType), 100)
return
}
isShowing = true
DOMQRCodeScanSuccessImage.src = imageType === "onscan" ? URLScanSuccessImage : URLEnterGateImage
DOMFullScreenMask.classList.remove("hidden")
DOMScanResultContainer.classList.remove("hidden")
//
setTimeout(() => {
DOMQRCodeScanSuccessImage.classList.add("scan-result-image-show")
}, 100)
setTimeout(function hideResult() {
console.log("hideResult")
DOMQRCodeScanSuccessImage.classList.remove("scan-result-image-show")
setTimeout(() => {
DOMQRCodeScanSuccessImage.src = ""
DOMFullScreenMask.classList.add("hidden")
DOMScanResultContainer.classList.add("hidden")
isShowing = false
}, 300)
}, 3000)
}
// setTimeout(showResult, 100)
window.showResult = showResult

View File

@ -1,6 +1,12 @@
window.wsUrl = 'wss://epp-prod.only4.work/access/websocket/1'; /**
// window.wsUrl = 'ws://127.0.0.1:80/access/websocket/1'; * FIXME 环境配置
// window.wsUrl = 'ws://127.0.0.1:8002/access/websocket/1'; *
* window.wsUrl
* - 线上环境'wss://epp.only4.work/access/websocket/'
* - 开发环境'ws://127.0.0.1:80/access/websocket/'
* 'ws://127.0.0.1:8002/access/websocket/'
*/
window.wsUrl = 'wss://epp.only4.work/access/websocket/';
window.ws = null; // WebSocket 实例对象 window.ws = null; // WebSocket 实例对象
@ -10,9 +16,20 @@ window.ws = null; // WebSocket 实例对象
return return
} }
function getWsUrl() {
function getUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
return `${window.wsUrl}${getUUID()}`
}
function createConn() { function createConn() {
// 创建webscoket 对象 // 创建webscoket 对象
const ws = new WebSocket(window.wsUrl) const ws = new WebSocket(getWsUrl() /* window.wsUrl */)
// 执行上面的语句之后,客户端就会与服务器进行连接 // 执行上面的语句之后,客户端就会与服务器进行连接
// readyState返回当前实例对象的当前状态 // readyState返回当前实例对象的当前状态
@ -34,9 +51,23 @@ window.ws = null; // WebSocket 实例对象
ws.onmessage = ({data}) => { ws.onmessage = ({data}) => {
console.log('onmessage readyState', ws.readyState) console.log('onmessage readyState', ws.readyState)
// 注意此时的data是json格式的 需要转化下 // 注意此时的data是json格式的 需要转化下
console.log('onmessage 有新消息啦=======>', JSON.parse(data)) let result = JSON.parse(data)
console.log('onmessage 有新消息啦=======>', result)
// 实例对象的send方法给服务器发送消息 // 实例对象的send方法给服务器发送消息
ws.send('客户端发送的消息') ws.send('客户端发送的消息')
switch (result.action) {
case 'onscan':
case 'onopen':
if (window.currentGate && result.gateId === window.currentGate.id) {
console.log(result.action)
window.showResult(result.action)
}
break;
default:
console.log("switch=>default", result.action)
break;
}
} }
// 实例对象的onclose属性用于连接关闭后的回调 函数 // 实例对象的onclose属性用于连接关闭后的回调 函数
@ -58,5 +89,6 @@ window.ws = null; // WebSocket 实例对象
} }
return ws return ws
} }
window.ws = createConn() window.ws = createConn()
})() })()

View File

@ -0,0 +1,33 @@
<?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 width="100" height="100" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="50" cy="50" r="45" stroke="white" stroke-width="7" fill="white"/>
<!-- 外围圆圈 -->
<circle cx="50" cy="50" r="45" stroke="#006600" stroke-width="8" fill="none">
<animate id="circle" attributeName="stroke-dasharray" from="0,282.743" to="282.743,0" dur="1s" begin="0s" fill="freeze"/>
<animateTransform attributeName="transform" type="rotate" from="-90 50 50" to="270 50 50" dur="1s" begin="0s" fill="freeze"/>
</circle>
<!-- 对勾动画 -->
<path d="M20 50 L40 70 L80 30" stroke="#006600" stroke-width="8" fill="none" visibility="hidden">
<set attributeName="visibility" from="hidden" to="visible" begin="circle.end+0.1s"/>
<!-- 对勾开始动画 -->
<!-- <animate attributeName="stroke-dasharray" from="0,120" to="120,0" dur="1s" begin="circle.end+0.1s" repeatCount="indefinite"/> -->
<animate id="check" attributeName="stroke-dasharray" from="0,120" to="120,0" dur="1s" begin="circle.end+0.1s" repeatCount="1"/>
<!-- 对勾隐藏动画 -->
<set attributeName="visibility" from="visible" to="hidden" begin="check.end+0.1s"/>
</path>
<!-- 扫码成功 -->
<!-- <text x="50" y="55" font-size="20" text-anchor="middle" fill="#006600" visibility="hidden">
扫码成功
<set attributeName="visibility" from="hidden" to="visible" begin="check.end+0.1s"/>
</text> -->
<text x="50" y="50" font-weight="bold" text-anchor="middle" fill="#006600" visibility="hidden">
<tspan x="50" dx="3" dy="-3" font-size="26">大门</tspan>
<tspan x="50" dy="23" font-size="20">已开启</tspan>
<set attributeName="visibility" from="hidden" to="visible" begin="check.end+0.1s"/>
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,33 @@
<?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 width="100" height="100" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="50" cy="50" r="45" stroke="white" stroke-width="7" fill="white"/>
<!-- 外围圆圈 -->
<circle cx="50" cy="50" r="45" stroke="#006600" stroke-width="8" fill="none">
<animate id="circle" attributeName="stroke-dasharray" from="0,282.743" to="282.743,0" dur="1s" begin="0s" fill="freeze"/>
<animateTransform attributeName="transform" type="rotate" from="-90 50 50" to="270 50 50" dur="1s" begin="0s" fill="freeze"/>
</circle>
<!-- 对勾动画 -->
<path d="M20 50 L40 70 L80 30" stroke="#006600" stroke-width="8" fill="none" visibility="hidden">
<set attributeName="visibility" from="hidden" to="visible" begin="circle.end+0.1s"/>
<!-- 对勾开始动画 -->
<!-- <animate attributeName="stroke-dasharray" from="0,120" to="120,0" dur="1s" begin="circle.end+0.1s" repeatCount="indefinite"/> -->
<animate id="check" attributeName="stroke-dasharray" from="0,120" to="120,0" dur="1s" begin="circle.end+0.1s" repeatCount="1"/>
<!-- 对勾隐藏动画 -->
<set attributeName="visibility" from="visible" to="hidden" begin="check.end+0.1s"/>
</path>
<!-- 扫码成功 -->
<!-- <text x="50" y="55" font-size="20" text-anchor="middle" fill="#006600" visibility="hidden">
扫码成功
<set attributeName="visibility" from="hidden" to="visible" begin="check.end+0.1s"/>
</text> -->
<text x="50" y="50" font-size="27" font-weight="bold" text-anchor="middle" fill="#006600" visibility="hidden">
<tspan x="50" dx="4" dy="-5">扫码</tspan>
<tspan x="50" dy="28">成功</tspan>
<set attributeName="visibility" from="hidden" to="visible" begin="check.end+0.1s"/>
</text>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -10,6 +10,7 @@
<title>社区疫情防控系统 - 门禁端</title> <title>社区疫情防控系统 - 门禁端</title>
<link rel="stylesheet" href="./assets/css/index.css"/> <link rel="stylesheet" href="./assets/css/index.css"/>
<link rel="stylesheet" href="./assets/css/setting-panel.css"/> <link rel="stylesheet" href="./assets/css/setting-panel.css"/>
<link rel="stylesheet" href="./assets/css/scan-result.css"/>
</head> </head>
<body> <body>
@ -36,11 +37,12 @@
</div> </div>
<div class="right-container"> <div class="right-container">
<h1 id="no-qrcode">请选择大门</h1> <h1 id="no-qrcode">请选择大门</h1>
<img id="qrcode" src="" style="display: none"> <img id="qrcode" src="" style="display: none;"><br>
<p id="refreshTimeCountDown"></p> <p id="refreshTimeCountDown"></p>
</div> </div>
</div> </div>
<!-- 设置页面 -->
<div id="full-screen-mask" class="full-screen-mask hidden"></div> <div id="full-screen-mask" class="full-screen-mask hidden"></div>
<div id="setting-container" class="setting-container hidden"> <div id="setting-container" class="setting-container hidden">
<div class="setting-panel"> <div class="setting-panel">
@ -63,8 +65,15 @@
</div> </div>
</div> </div>
</div> </div>
<!-- 扫码成功 -->
<div id="scan-result-container" class="scan-result-container hidden">
<img id="qrcode-scan-success" class="scan-result-image" src="./assets/svg/scan-success.svg">
</div>
<script src="./assets/js/setting-panel.js" type="module"></script> <script src="./assets/js/setting-panel.js" type="module"></script>
<script src="./renderer.js" type="module"></script> <script src="./renderer.js" type="module"></script>
<script src="assets/js/websocket-message-panel.js"></script>
<script src="./assets/js/websocket.js"></script> <script src="./assets/js/websocket.js"></script>
</body> </body>

View File

@ -1,3 +1,13 @@
/**
* FIXME 环境配置
*
* baseUrl
* - 线上环境"https://epp.only4.work/"
* - 开发环境"/"
* envVersion
* - 线上环境"release"
* - 开发环境"develop"
*/
// 定义常量 // 定义常量
const baseUrl = "https://epp-prod.only4.work/" const baseUrl = "https://epp-prod.only4.work/"
const url = baseUrl + "access/wechat/getUnlimitedQRCode" const url = baseUrl + "access/wechat/getUnlimitedQRCode"

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>基于微服务的社区疫情防控系统</title>
<style>
body {
font-size: 1.2em;
}
.grid-item {
border: 1px solid black;
text-align: center;
}
.grid-item ul {
text-align: left;
}
</style>
</head>
<body>
<div>
<h1 style="text-align: center;">基于微服务的社区疫情防控系统</h1>
<p style="text-align: center;">epp.only4.work</p>
<div style="display: grid; grid-template-columns: repeat(2, 1fr);">
<div class="grid-item">
<h3>
门禁端 跨端桌面应用
</h3>
<ul>
<li>
方式1访问
<a target="_blank" href="./guard/index.html">https://epp.only4.work/guard/index.html</a>
</li>
<li>
方式2运行跨端桌面应用
</li>
</ul>
</div>
<div class="grid-item">
<h3>
居民端 小程序
</h3>
<ul>
<li>
方式1扫描下方小程序码
<p style="text-align: center">
<img src="./assets/image/miniprogram.jpg" style="width: 180px;">
</p>
</li>
<li>
方式2微信搜索小程序 <b>devprogram</b>
</li>
</ul>
</div>
<div class="grid-item">
<h3>
社区管理员端 管理后台
</h3>
<ul>
<li>
访问
<a target="_blank" href="./manage/index.html">https://epp.only4.work/manage/index.html</a>
</li>
</ul>
</div>
<div class="grid-item">
<h3>
系统管理员端 管理后台
</h3>
<ul>
<li>
访问
<a target="_blank" href="./manage/index.html">https://epp.only4.work/manage/index.html</a>
</li>
</ul>
</div>
</div>
</div>
<div>
<h2>开发</h2>
<p>小程序后台:<a target="_blank">https://mp.weixin.qq.com/</a></p>
</div>
</body>
</html>

View File

@ -0,0 +1 @@
import{d as r,dJ as p,b0 as l,r as i,o as m,c as E,e as o,b as s,w as a,p as f,h,f as t,_ as v}from"./index.f859bf4e.js";const c=e=>(f("data-v-d4771405"),e=e(),h(),e),B={class:"error-page"},g=c(()=>o("div",{class:"error-code"},[t("4"),o("span",null,"0"),t("3")],-1)),k=c(()=>o("div",{class:"error-desc"},"\u554A\u54E6~ \u4F60\u6CA1\u6709\u6743\u9650\u8BBF\u95EE\u8BE5\u9875\u9762\u54E6",-1)),x={class:"error-handle"},y=r({name:"403"}),C=r({...y,setup(e){const u=p(),n=()=>{u.go(-2)};return(b,D)=>{const _=l,d=i("router-link");return m(),E("div",B,[g,k,o("div",x,[s(d,{to:"/"},{default:a(()=>[s(_,{type:"primary",size:"large"},{default:a(()=>[t("\u8FD4\u56DE\u9996\u9875")]),_:1})]),_:1}),s(_,{class:"error-btn",type:"primary",size:"large",onClick:n},{default:a(()=>[t("\u8FD4\u56DE\u4E0A\u4E00\u9875")]),_:1})])])}}});const I=v(C,[["__scopeId","data-v-d4771405"]]);export{I as default};

View File

@ -0,0 +1 @@
.error-page[data-v-d4771405]{display:flex;justify-content:center;align-items:center;flex-direction:column;width:100%;height:100%;background:#f3f3f3;box-sizing:border-box}.error-code[data-v-d4771405]{line-height:1;font-size:250px;font-weight:bolder;color:#f02d2d}.error-code span[data-v-d4771405]{color:#00a854}.error-desc[data-v-d4771405]{font-size:30px;color:#777}.error-handle[data-v-d4771405]{margin-top:30px;padding-bottom:200px}.error-btn[data-v-d4771405]{margin-left:100px}

View File

@ -0,0 +1 @@
import{m as s}from"./manage-list.8ab8e06b.js";import{s as t}from"./send_request.cc43fdb9.js";import{d as n,o as r,c,b as o}from"./index.f859bf4e.js";import"./el-overlay.1542ee54.js";import"./el-input.f35758e8.js";import"./el-progress.d53d438b.js";function u(e){return t({url:"/access/gate/manage/getGateList",method:"GET",params:e})}function a(e){return t({url:"/access/gate/manage/editGate",method:"POST",useQS:!0,params:e})}function i(e){return t({url:"/access/gate/manage/deleteGate",method:"POST",useQS:!0,params:e})}function m(e){return t({url:"/access/gate/manage/exportGateList",method:"GET",params:e})}const d={class:"container"},L=n({__name:"access-gate-setting",setup(e){return(p,f)=>(r(),c("div",d,[o(s,{"list-func":u,"add-func":a,"edit-func":a,"delete-func":i,"export-func":m,"edit-permiss":"access-gate-setting"},null,8,["list-func","add-func","edit-func","delete-func","export-func"])]))}});export{L as default};

View File

@ -0,0 +1 @@
import{m as o}from"./manage-list.8ab8e06b.js";import{s as m}from"./send_request.cc43fdb9.js";import{d as r,o as c,c as a,b as n}from"./index.f859bf4e.js";import"./el-overlay.1542ee54.js";import"./el-input.f35758e8.js";import"./el-progress.d53d438b.js";function p(e){var s,i;let t=JSON.parse(JSON.stringify(e));return delete t.timestamp,Array.isArray(e.timestamp)&&e.timestamp.length==2&&(t.startTime=(s=e.timestamp[0])==null?void 0:s.getTime(),t.endTime=(i=e.timestamp[1])==null?void 0:i.getTime()),m({url:"/access/access-log/manage/getAccessLogList",method:"GET",params:t})}function g(e){var s,i;let t=JSON.parse(JSON.stringify(e));return delete t.timestamp,Array.isArray(e.timestamp)&&e.timestamp.length==2&&(t.startTime=(s=e.timestamp[0])==null?void 0:s.getTime(),t.endTime=(i=e.timestamp[1])==null?void 0:i.getTime()),m({url:"/access/access-log/manage/exportAccessLogList",method:"GET",params:t})}const l={class:"container"},y=r({__name:"access-log",setup(e){return(t,s)=>(c(),a("div",l,[n(o,{"list-func":p,"export-func":g,"edit-permiss":"access-log"},null,8,["list-func","export-func"])]))}});export{y as default};

View File

@ -0,0 +1 @@
.el-row[data-v-e670d298]{margin-bottom:20px}.grid-content[data-v-e670d298]{display:flex;align-items:center;height:100px}.grid-cont-right[data-v-e670d298]{flex:1;text-align:center;font-size:14px;color:#999}.grid-num[data-v-e670d298]{font-size:30px;font-weight:700}.grid-con-icon[data-v-e670d298]{font-size:50px;width:100px;height:100px;text-align:center;line-height:100px;color:#fff}.grid-con-1 .grid-con-icon[data-v-e670d298]{background:rgb(45,140,240)}.grid-con-1 .grid-num[data-v-e670d298]{color:#2d8cf0}.grid-con-2 .grid-con-icon[data-v-e670d298]{background:rgb(100,213,114)}.grid-con-2 .grid-num[data-v-e670d298]{color:#64d572}.grid-con-3 .grid-con-icon[data-v-e670d298]{background:rgb(242,94,67)}.grid-con-3 .grid-num[data-v-e670d298]{color:#f25e43}.user-info[data-v-e670d298]{display:flex;align-items:center;padding-bottom:20px;border-bottom:2px solid #ccc;margin-bottom:20px}.user-info-cont[data-v-e670d298]{padding-left:50px;flex:1;font-size:14px;color:#999}.user-info-cont div[data-v-e670d298]:first-child{font-size:30px;color:#222}.user-info-list[data-v-e670d298]{font-size:14px;color:#999;line-height:25px}.user-info-list span[data-v-e670d298]{margin-left:70px}.mgb20[data-v-e670d298]{margin-bottom:20px}.todo-item[data-v-e670d298]{font-size:14px}.todo-item-del[data-v-e670d298]{text-decoration:line-through;color:#999}

View File

@ -0,0 +1 @@
import{d as g,E as C,r as _,a as F,o as x,c as y,b as s,w as e,e as o,u as r,t as h,f as t,g as b,p as w,h as A,_ as S}from"./index.f859bf4e.js";import{E as D,a as I,b as V}from"./el-card.0035c23b.js";import{E as N}from"./el-progress.d53d438b.js";const a=d=>(w("data-v-e670d298"),d=d(),A(),d),R={class:"container"},T={class:"user-info"},k={class:"user-info-cont"},G={class:"user-info-name"},U=a(()=>o("div",{class:"user-info-list"},[t(" \u4E0A\u6B21\u767B\u5F55\u65F6\u95F4\uFF1A "),o("span",null,"2022-10-01")],-1)),z=a(()=>o("div",{class:"user-info-list"},[t(" \u4E0A\u6B21\u767B\u5F55\u5730\u70B9\uFF1A "),o("span",null,"\u4E1C\u839E")],-1)),H=a(()=>o("div",{class:"clearfix"},[o("span",null,"\u8BED\u8A00\u8BE6\u60C5")],-1)),L={class:"grid-content grid-con-1"},M=a(()=>o("div",{class:"grid-cont-right"},[o("div",{class:"grid-num"},"1234"),o("div",null,"\u7528\u6237\u8BBF\u95EE\u91CF")],-1)),P={class:"grid-content grid-con-2"},j=a(()=>o("div",{class:"grid-cont-right"},[o("div",{class:"grid-num"},"321"),o("div",null,"\u7CFB\u7EDF\u6D88\u606F")],-1)),q={class:"grid-content grid-con-3"},J=a(()=>o("div",{class:"grid-cont-right"},[o("div",{class:"grid-num"},"500"),o("div",null,"\u5546\u54C1\u6570\u91CF")],-1)),K=g({name:"dashboard"}),O=g({...K,setup(d){const i=localStorage.getItem("ms_username"),f=i==="admin"?"\u8D85\u7EA7\u7BA1\u7406\u5458":"\u666E\u901A\u7528\u6237";return(Q,W)=>{const v=C,n=D,l=N,c=I,m=_("User"),u=F,E=_("ChatDotRound"),B=_("Goods"),p=V;return x(),y("div",R,[s(p,{gutter:20},{default:e(()=>[s(c,{span:8},{default:e(()=>[s(n,{shadow:"hover",class:"mgb20",style:{height:"252px"}},{default:e(()=>[o("div",T,[s(v,{size:120,src:r(b)},null,8,["src"]),o("div",k,[o("div",G,h(r(i)),1),o("div",null,h(r(f)),1)])]),U,z]),_:1}),s(n,{shadow:"hover",style:{height:"252px"}},{header:e(()=>[H]),default:e(()=>[t(" Vue "),s(l,{percentage:79.4,color:"#42b983"},null,8,["percentage"]),t(" TypeScript "),s(l,{percentage:14,color:"#f1e05a"}),t(" CSS "),s(l,{percentage:5.6},null,8,["percentage"]),t(" HTML "),s(l,{percentage:1,color:"#f56c6c"})]),_:1})]),_:1}),s(c,{span:16},{default:e(()=>[s(p,{gutter:20,class:"mgb20"},{default:e(()=>[s(c,{span:8},{default:e(()=>[s(n,{shadow:"hover","body-style":{padding:"0px"}},{default:e(()=>[o("div",L,[s(u,{class:"grid-con-icon"},{default:e(()=>[s(m)]),_:1}),M])]),_:1})]),_:1}),s(c,{span:8},{default:e(()=>[s(n,{shadow:"hover","body-style":{padding:"0px"}},{default:e(()=>[o("div",P,[s(u,{class:"grid-con-icon"},{default:e(()=>[s(E)]),_:1}),j])]),_:1})]),_:1}),s(c,{span:8},{default:e(()=>[s(n,{shadow:"hover","body-style":{padding:"0px"}},{default:e(()=>[o("div",q,[s(u,{class:"grid-con-icon"},{default:e(()=>[s(B)]),_:1}),J])]),_:1})]),_:1})]),_:1})]),_:1})]),_:1})])}}});const $=S(O,[["__scopeId","data-v-e670d298"]]);export{$ as default};

View File

@ -0,0 +1 @@
import{i as g,j as r,d as u,k as h,o as f,c as N,n as d,u as o,l as m,f as R,t as x,m as B,e as K,q as w,s as _,v,x as p,y as P,z as n,A as j,B as A,C as S,w as k,D as E,F as D}from"./index.f859bf4e.js";const O=Symbol("rowContextKey"),V=g({header:{type:String,default:""},bodyStyle:{type:r([String,Object,Array]),default:""},shadow:{type:String,values:["always","hover","never"],default:"always"}}),L=u({name:"ElCard"}),T=u({...L,props:V,setup(i){const t=h("card");return(s,l)=>(f(),N("div",{class:d([o(t).b(),o(t).is(`${s.shadow}-shadow`)])},[s.$slots.header||s.header?(f(),N("div",{key:0,class:d(o(t).e("header"))},[m(s.$slots,"header",{},()=>[R(x(s.header),1)])],2)):B("v-if",!0),K("div",{class:d(o(t).e("body")),style:w(s.bodyStyle)},[m(s.$slots,"default")],6)],2))}});var q=_(T,[["__file","/home/runner/work/element-plus/element-plus/packages/components/card/src/card.vue"]]);const z=v(q),F=g({tag:{type:String,default:"div"},span:{type:Number,default:24},offset:{type:Number,default:0},pull:{type:Number,default:0},push:{type:Number,default:0},xs:{type:r([Number,Object]),default:()=>p({})},sm:{type:r([Number,Object]),default:()=>p({})},md:{type:r([Number,Object]),default:()=>p({})},lg:{type:r([Number,Object]),default:()=>p({})},xl:{type:r([Number,Object]),default:()=>p({})}}),I=u({name:"ElCol"}),J=u({...I,props:F,setup(i){const t=i,{gutter:s}=P(O,{gutter:n(()=>0)}),l=h("col"),y=n(()=>{const e={};return s.value&&(e.paddingLeft=e.paddingRight=`${s.value/2}px`),e}),b=n(()=>{const e=[];return["span","offset","pull","push"].forEach(a=>{const c=t[a];j(c)&&(a==="span"?e.push(l.b(`${t[a]}`)):c>0&&e.push(l.b(`${a}-${t[a]}`)))}),["xs","sm","md","lg","xl"].forEach(a=>{j(t[a])?e.push(l.b(`${a}-${t[a]}`)):A(t[a])&&Object.entries(t[a]).forEach(([c,C])=>{e.push(c!=="span"?l.b(`${a}-${c}-${C}`):l.b(`${a}-${C}`))})}),s.value&&e.push(l.is("guttered")),[l.b(),e]});return(e,$)=>(f(),S(E(e.tag),{class:d(o(b)),style:w(o(y))},{default:k(()=>[m(e.$slots,"default")]),_:3},8,["class","style"]))}});var G=_(J,[["__file","/home/runner/work/element-plus/element-plus/packages/components/col/src/col.vue"]]);const ee=v(G),H=["start","center","end","space-around","space-between","space-evenly"],M=["top","middle","bottom"],Q=g({tag:{type:String,default:"div"},gutter:{type:Number,default:0},justify:{type:String,values:H,default:"start"},align:{type:String,values:M,default:"top"}}),U=u({name:"ElRow"}),W=u({...U,props:Q,setup(i){const t=i,s=h("row"),l=n(()=>t.gutter);D(O,{gutter:l});const y=n(()=>{const e={};return t.gutter&&(e.marginRight=e.marginLeft=`-${t.gutter/2}px`),e}),b=n(()=>[s.b(),s.is(`justify-${t.justify}`,t.justify!=="start"),s.is(`align-${t.align}`,t.align!=="top")]);return(e,$)=>(f(),S(E(e.tag),{class:d(o(b)),style:w(o(y))},{default:k(()=>[m(e.$slots,"default")]),_:3},8,["class","style"]))}});var X=_(W,[["__file","/home/runner/work/element-plus/element-plus/packages/components/row/src/row.vue"]]);const te=v(X);export{z as E,ee as a,te as b};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
:root{--el-popup-modal-bg-color:var(--el-color-black);--el-popup-modal-opacity:.5}.v-modal-enter{-webkit-animation:v-modal-in var(--el-transition-duration-fast) ease;animation:v-modal-in var(--el-transition-duration-fast) ease}.v-modal-leave{-webkit-animation:v-modal-out var(--el-transition-duration-fast) ease forwards;animation:v-modal-out var(--el-transition-duration-fast) ease forwards}@-webkit-keyframes v-modal-in{0%{opacity:0}}@keyframes v-modal-in{0%{opacity:0}}@-webkit-keyframes v-modal-out{to{opacity:0}}@keyframes v-modal-out{to{opacity:0}}.v-modal{position:fixed;left:0;top:0;width:100%;height:100%;opacity:var(--el-popup-modal-opacity);background:var(--el-popup-modal-bg-color)}.el-popup-parent--hidden{overflow:hidden}.el-dialog{--el-dialog-width:50%;--el-dialog-margin-top:15vh;--el-dialog-bg-color:var(--el-bg-color);--el-dialog-box-shadow:var(--el-box-shadow);--el-dialog-title-font-size:var(--el-font-size-large);--el-dialog-content-font-size:14px;--el-dialog-font-line-height:var(--el-font-line-height-primary);--el-dialog-padding-primary:20px;--el-dialog-border-radius:var(--el-border-radius-small);position:relative;margin:var(--el-dialog-margin-top,15vh) auto 50px;background:var(--el-dialog-bg-color);border-radius:var(--el-dialog-border-radius);box-shadow:var(--el-dialog-box-shadow);box-sizing:border-box;width:var(--el-dialog-width,50%)}.el-dialog:focus{outline:0!important}.el-dialog.is-align-center{margin:auto}.el-dialog.is-fullscreen{--el-dialog-width:100%;--el-dialog-margin-top:0;margin-bottom:0;height:100%;overflow:auto}.el-dialog__wrapper{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto;margin:0}.el-dialog.is-draggable .el-dialog__header{cursor:move;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.el-dialog__header{padding:var(--el-dialog-padding-primary);padding-bottom:10px;margin-right:16px}.el-dialog__headerbtn{position:absolute;top:6px;right:0;padding:0;width:54px;height:54px;background:0 0;border:none;outline:0;cursor:pointer;font-size:var(--el-message-close-size,16px)}.el-dialog__headerbtn .el-dialog__close{color:var(--el-color-info);font-size:inherit}.el-dialog__headerbtn:focus .el-dialog__close,.el-dialog__headerbtn:hover .el-dialog__close{color:var(--el-color-primary)}.el-dialog__title{line-height:var(--el-dialog-font-line-height);font-size:var(--el-dialog-title-font-size);color:var(--el-text-color-primary)}.el-dialog__body{padding:calc(var(--el-dialog-padding-primary) + 10px) var(--el-dialog-padding-primary);color:var(--el-text-color-regular);font-size:var(--el-dialog-content-font-size)}.el-dialog__footer{padding:var(--el-dialog-padding-primary);padding-top:10px;text-align:right;box-sizing:border-box}.el-dialog--center{text-align:center}.el-dialog--center .el-dialog__body{text-align:initial;padding:25px calc(var(--el-dialog-padding-primary) + 5px) 30px}.el-dialog--center .el-dialog__footer{text-align:inherit}.el-overlay-dialog{position:fixed;top:0;right:0;bottom:0;left:0;overflow:auto}.dialog-fade-enter-active{-webkit-animation:modal-fade-in var(--el-transition-duration);animation:modal-fade-in var(--el-transition-duration)}.dialog-fade-enter-active .el-overlay-dialog{-webkit-animation:dialog-fade-in var(--el-transition-duration);animation:dialog-fade-in var(--el-transition-duration)}.dialog-fade-leave-active{-webkit-animation:modal-fade-out var(--el-transition-duration);animation:modal-fade-out var(--el-transition-duration)}.dialog-fade-leave-active .el-overlay-dialog{-webkit-animation:dialog-fade-out var(--el-transition-duration);animation:dialog-fade-out var(--el-transition-duration)}@-webkit-keyframes dialog-fade-in{0%{transform:translate3d(0,-20px,0);opacity:0}to{transform:translateZ(0);opacity:1}}@keyframes dialog-fade-in{0%{transform:translate3d(0,-20px,0);opacity:0}to{transform:translateZ(0);opacity:1}}@-webkit-keyframes dialog-fade-out{0%{transform:translateZ(0);opacity:1}to{transform:translate3d(0,-20px,0);opacity:0}}@keyframes dialog-fade-out{0%{transform:translateZ(0);opacity:1}to{transform:translate3d(0,-20px,0);opacity:0}}@-webkit-keyframes modal-fade-in{0%{opacity:0}to{opacity:1}}@keyframes modal-fade-in{0%{opacity:0}to{opacity:1}}@-webkit-keyframes modal-fade-out{0%{opacity:1}to{opacity:0}}@keyframes modal-fade-out{0%{opacity:1}to{opacity:0}}.el-overlay{position:fixed;top:0;right:0;bottom:0;left:0;z-index:2000;height:100%;background-color:var(--el-overlay-color-lighter);overflow:auto}.el-overlay .el-overlay-root{height:0}

View File

@ -0,0 +1 @@
.el-progress{position:relative;line-height:1;display:flex;align-items:center}.el-progress__text{font-size:14px;color:var(--el-text-color-regular);margin-left:5px;min-width:50px;line-height:1}.el-progress__text i{vertical-align:middle;display:block}.el-progress--circle,.el-progress--dashboard{display:inline-block}.el-progress--circle .el-progress__text,.el-progress--dashboard .el-progress__text{position:absolute;top:50%;left:0;width:100%;text-align:center;margin:0;transform:translateY(-50%)}.el-progress--circle .el-progress__text i,.el-progress--dashboard .el-progress__text i{vertical-align:middle;display:inline-block}.el-progress--without-text .el-progress__text{display:none}.el-progress--without-text .el-progress-bar{padding-right:0;margin-right:0;display:block}.el-progress--text-inside .el-progress-bar{padding-right:0;margin-right:0}.el-progress.is-success .el-progress-bar__inner{background-color:var(--el-color-success)}.el-progress.is-success .el-progress__text{color:var(--el-color-success)}.el-progress.is-warning .el-progress-bar__inner{background-color:var(--el-color-warning)}.el-progress.is-warning .el-progress__text{color:var(--el-color-warning)}.el-progress.is-exception .el-progress-bar__inner{background-color:var(--el-color-danger)}.el-progress.is-exception .el-progress__text{color:var(--el-color-danger)}.el-progress-bar{flex-grow:1;box-sizing:border-box}.el-progress-bar__outer{height:6px;border-radius:100px;background-color:var(--el-border-color-lighter);overflow:hidden;position:relative;vertical-align:middle}.el-progress-bar__inner{position:absolute;left:0;top:0;height:100%;background-color:var(--el-color-primary);text-align:right;border-radius:100px;line-height:1;white-space:nowrap;transition:width .6s ease}.el-progress-bar__inner:after{display:inline-block;content:"";height:100%;vertical-align:middle}.el-progress-bar__inner--indeterminate{transform:translateZ(0);-webkit-animation:indeterminate 3s infinite;animation:indeterminate 3s infinite}.el-progress-bar__innerText{display:inline-block;vertical-align:middle;color:#fff;font-size:12px;margin:0 5px}@-webkit-keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}@keyframes progress{0%{background-position:0 0}to{background-position:32px 0}}@-webkit-keyframes indeterminate{0%{left:-100%}to{left:100%}}@keyframes indeterminate{0%{left:-100%}to{left:100%}}

View File

@ -0,0 +1,6 @@
import{i as M,j as m,d as T,k as A,z as r,G as V,H as O,I as j,J as q,K as G,L as H,M as C,o as l,c,n as i,u as t,e as y,q as f,l as _,t as D,m as I,C as N,w as J,D as K,a as R,s as U,v as Q}from"./index.f859bf4e.js";const X=M({type:{type:String,default:"line",values:["line","circle","dashboard"]},percentage:{type:Number,default:0,validator:u=>u>=0&&u<=100},status:{type:String,default:"",values:["","success","exception","warning"]},indeterminate:{type:Boolean,default:!1},duration:{type:Number,default:3},strokeWidth:{type:Number,default:6},strokeLinecap:{type:m(String),default:"round"},textInside:{type:Boolean,default:!1},width:{type:Number,default:126},showText:{type:Boolean,default:!0},color:{type:m([String,Array,Function]),default:""},format:{type:m(Function),default:u=>`${u}%`}}),Y=["aria-valuenow"],Z={viewBox:"0 0 100 100"},ee=["d","stroke","stroke-width"],te=["d","stroke","opacity","stroke-linecap","stroke-width"],se={key:0},ae=T({name:"ElProgress"}),re=T({...ae,props:X,setup(u){const s=u,v={success:"#13ce66",exception:"#ff4949",warning:"#e6a23c",default:"#20a0ff"},a=A("progress"),x=r(()=>({width:`${s.percentage}%`,animationDuration:`${s.duration}s`,backgroundColor:P(s.percentage)})),g=r(()=>(s.strokeWidth/s.width*100).toFixed(1)),b=r(()=>["circle","dashboard"].includes(s.type)?Number.parseInt(`${50-Number.parseFloat(g.value)/2}`,10):0),$=r(()=>{const e=b.value,o=s.type==="dashboard";return`
M 50 50
m 0 ${o?"":"-"}${e}
a ${e} ${e} 0 1 1 0 ${o?"-":""}${e*2}
a ${e} ${e} 0 1 1 0 ${o?"":"-"}${e*2}
`}),h=r(()=>2*Math.PI*b.value),k=r(()=>s.type==="dashboard"?.75:1),w=r(()=>`${-1*h.value*(1-k.value)/2}px`),B=r(()=>({strokeDasharray:`${h.value*k.value}px, ${h.value}px`,strokeDashoffset:w.value})),z=r(()=>({strokeDasharray:`${h.value*k.value*(s.percentage/100)}px, ${h.value}px`,strokeDashoffset:w.value,transition:"stroke-dasharray 0.6s ease 0s, stroke 0.6s ease, opacity ease 0.6s"})),E=r(()=>{let e;return s.color?e=P(s.percentage):e=v[s.status]||v.default,e}),F=r(()=>s.status==="warning"?V:s.type==="line"?s.status==="success"?O:j:s.status==="success"?q:G),W=r(()=>s.type==="line"?12+s.strokeWidth*.4:s.width*.111111+2),S=r(()=>s.format(s.percentage));function L(e){const o=100/e.length;return e.map((n,p)=>C(n)?{color:n,percentage:(p+1)*o}:n).sort((n,p)=>n.percentage-p.percentage)}const P=e=>{var o;const{color:d}=s;if(H(d))return d(e);if(C(d))return d;{const n=L(d);for(const p of n)if(p.percentage>e)return p.color;return(o=n[n.length-1])==null?void 0:o.color}};return(e,o)=>(l(),c("div",{class:i([t(a).b(),t(a).m(e.type),t(a).is(e.status),{[t(a).m("without-text")]:!e.showText,[t(a).m("text-inside")]:e.textInside}]),role:"progressbar","aria-valuenow":e.percentage,"aria-valuemin":"0","aria-valuemax":"100"},[e.type==="line"?(l(),c("div",{key:0,class:i(t(a).b("bar"))},[y("div",{class:i(t(a).be("bar","outer")),style:f({height:`${e.strokeWidth}px`})},[y("div",{class:i([t(a).be("bar","inner"),{[t(a).bem("bar","inner","indeterminate")]:e.indeterminate}]),style:f(t(x))},[(e.showText||e.$slots.default)&&e.textInside?(l(),c("div",{key:0,class:i(t(a).be("bar","innerText"))},[_(e.$slots,"default",{percentage:e.percentage},()=>[y("span",null,D(t(S)),1)])],2)):I("v-if",!0)],6)],6)],2)):(l(),c("div",{key:1,class:i(t(a).b("circle")),style:f({height:`${e.width}px`,width:`${e.width}px`})},[(l(),c("svg",Z,[y("path",{class:i(t(a).be("circle","track")),d:t($),stroke:`var(${t(a).cssVarName("fill-color-light")}, #e5e9f2)`,"stroke-width":t(g),fill:"none",style:f(t(B))},null,14,ee),y("path",{class:i(t(a).be("circle","path")),d:t($),stroke:t(E),fill:"none",opacity:e.percentage?1:0,"stroke-linecap":e.strokeLinecap,"stroke-width":t(g),style:f(t(z))},null,14,te)]))],6)),(e.showText||e.$slots.default)&&!e.textInside?(l(),c("div",{key:2,class:i(t(a).e("text")),style:f({fontSize:`${t(W)}px`})},[_(e.$slots,"default",{percentage:e.percentage},()=>[e.status?(l(),N(t(R),{key:1},{default:J(()=>[(l(),N(K(t(F))))]),_:1})):(l(),c("span",se,D(t(S)),1))])],6)):I("v-if",!0)],10,Y))}});var oe=U(re,[["__file","/home/runner/work/element-plus/element-plus/packages/components/progress/src/progress.vue"]]);const le=Q(oe);export{le as E};

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
.login-wrap[data-v-0fd4c22f]{width:100%;height:100%}.login-container[data-v-0fd4c22f]{width:100%;height:100%;display:grid;place-items:center}.ms-title[data-v-0fd4c22f]{width:100%;padding:18px 24px;box-sizing:border-box;text-align:center;font-size:20px;color:#fff;border-bottom:1px solid #ddd}.ms-login[data-v-0fd4c22f]{width:min(380px,95vw);padding:5px 10px;border-radius:5px;background:rgba(255,255,255,.3);overflow:hidden}.ms-content[data-v-0fd4c22f]{padding:30px}.login-btn[data-v-0fd4c22f]{text-align:center}.login-btn button[data-v-0fd4c22f]{width:100%;height:36px;margin-bottom:10px}.company-info[data-v-0fd4c22f]{color:#7589b6;text-align:center;position:absolute;left:0;right:0;bottom:10px;font-size:13px;letter-spacing:1px}

View File

@ -0,0 +1 @@
import{d as E,dJ as x,aR as C,ac as k,dK as N,r as R,o as b,c as h,e as d,t as F,u,b as o,w as a,aT as S,f as T,m as q,bP as B,b0 as D,a as K,bX as f,dL as L,dM as M,_ as U}from"./index.f859bf4e.js";import{E as $,s as j,t as A}from"./el-input.f35758e8.js";import{u as J}from"./user.0bac8e4f.js";import"./send_request.cc43fdb9.js";const O={class:"login-wrap"},P={class:"login-container"},X={class:"ms-login"},z={class:"ms-title"},G={class:"login-btn"},H={key:0,class:"company-info"},Q=E({__name:"login",setup(W){const _=x(),r=C({username:"root",password:"root"}),V={username:[{required:!0,message:"\u8BF7\u8F93\u5165\u7528\u6237\u540D",trigger:"blur"}],password:[{required:!0,message:"\u8BF7\u8F93\u5165\u5BC6\u7801",trigger:"blur"}]},p=k(),v=g=>{!g||g.validate(async(s,l)=>{if(!s){console.log("invalidFields",l),Object.values(l).forEach(e=>{e.forEach(t=>{B.error({message:t.message,grouping:!0})})});return}J({username:r.username,password:r.password}).then(async e=>{var i,c,m,n,y,I,w;if(!e)return;console.log("login data",e,e.userInfo),B.success("\u767B\u5F55\u6210\u529F"),localStorage.setItem("ms_username",(i=e.userInfo)==null?void 0:i.username),localStorage.setItem("ms_realname",(c=e.userInfo)==null?void 0:c.realname),localStorage.setItem("ms_user_id",(m=e.userInfo)==null?void 0:m.id),localStorage.setItem("ms_role_id",(n=e.userInfo)==null?void 0:n.roleId);let t=(w=(I=(y=_.currentRoute)==null?void 0:y.value)==null?void 0:I.query)==null?void 0:w.redirectTo;t&&!t.includes("/login")?_.push(t):_.push("/")})})};return N().clearTags(),(g,s)=>{const l=D,e=$,t=j,i=R("Right"),c=K,m=A;return b(),h("div",O,[d("div",P,[d("div",X,[d("div",z,F(u(f).siteFullTitle),1),o(m,{model:r,rules:V,ref_key:"login",ref:p,"label-width":"0px",class:"ms-content"},{default:a(()=>[o(t,{prop:"username"},{default:a(()=>[o(e,{modelValue:r.username,"onUpdate:modelValue":s[0]||(s[0]=n=>r.username=n),placeholder:"\u7528\u6237\u540D"},{prepend:a(()=>[o(l,{icon:u(L)},null,8,["icon"])]),_:1},8,["modelValue"])]),_:1}),o(t,{prop:"password"},{default:a(()=>[o(e,{type:"password",placeholder:"\u5BC6\u7801",modelValue:r.password,"onUpdate:modelValue":s[1]||(s[1]=n=>r.password=n),onKeyup:s[2]||(s[2]=S(n=>v(p.value),["enter"]))},{prepend:a(()=>[o(l,{icon:u(M)},null,8,["icon"])]),_:1},8,["modelValue"])]),_:1}),d("div",G,[o(l,{type:"primary",onClick:s[3]||(s[3]=n=>v(p.value))},{default:a(()=>[T(" \u767B \u5F55\xA0"),o(c,null,{default:a(()=>[o(i)]),_:1})]),_:1})])]),_:1},8,["model"])])]),u(f).companyName?(b(),h("div",H,F(u(f).companyName),1)):q("",!0)])}}});const te=U(Q,[["__scopeId","data-v-0fd4c22f"]]);export{te as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
import{m as t}from"./manage-list.8ab8e06b.js";import{g as s,e,d as r,a as i}from"./user.0bac8e4f.js";import{d as n,o,c as a,b as c}from"./index.f859bf4e.js";import"./el-overlay.1542ee54.js";import"./el-input.f35758e8.js";import"./el-progress.d53d438b.js";import"./send_request.cc43fdb9.js";const p={class:"container"},L=n({__name:"privilege-user-setting",setup(d){return(m,u)=>(o(),a("div",p,[c(t,{"list-func":s,"add-func":e,"edit-func":e,"delete-func":r,"export-func":i,"edit-permiss":"privilege-user-setting"},null,8,["list-func","add-func","edit-func","delete-func","export-func"])]))}});export{L as default};

View File

@ -0,0 +1 @@
import{m}from"./manage-list.8ab8e06b.js";import{s}from"./send_request.cc43fdb9.js";import{d as o,o as n,c as p,b as a}from"./index.f859bf4e.js";import"./el-overlay.1542ee54.js";import"./el-input.f35758e8.js";import"./el-progress.d53d438b.js";function c(e){var r,i;let t=JSON.parse(JSON.stringify(e));return delete t.timestamp,Array.isArray(e.timestamp)&&e.timestamp.length==2&&(t.startTime=(r=e.timestamp[0])==null?void 0:r.getTime(),t.endTime=(i=e.timestamp[1])==null?void 0:i.getTime()),s({url:"/access/report/manage/getReportList",method:"GET",params:t})}function l(e){var r,i;let t=JSON.parse(JSON.stringify(e));return delete t.timestamp,Array.isArray(e.timestamp)&&e.timestamp.length==2&&(t.startTime=(r=e.timestamp[0])==null?void 0:r.getTime(),t.endTime=(i=e.timestamp[1])==null?void 0:i.getTime()),s({url:"/access/report/manage/exportReportList",method:"GET",params:t})}const u={class:"container"},h=o({__name:"report-log",setup(e){return(t,r)=>(n(),p("div",u,[a(m,{"list-func":c,"export-func":l,"edit-permiss":"privilege-user-setting"},null,8,["list-func","export-func"])]))}});export{h as default};

View File

@ -0,0 +1 @@
import{bU as O,bV as S,bW as P,bX as j,bY as L,bP as b}from"./index.f859bf4e.js";import{v as N}from"./el-input.f35758e8.js";var o=O,g=Object.prototype.hasOwnProperty,x=Array.isArray,s={allowDots:!1,allowPrototypes:!1,allowSparse:!1,arrayLimit:20,charset:"utf-8",charsetSentinel:!1,comma:!1,decoder:o.decode,delimiter:"&",depth:5,ignoreQueryPrefix:!1,interpretNumericEntities:!1,parameterLimit:1e3,parseArrays:!0,plainObjects:!1,strictNullHandling:!1},E=function(i){return i.replace(/&#(\d+);/g,function(e,r){return String.fromCharCode(parseInt(r,10))})},v=function(i,e){return i&&typeof i=="string"&&e.comma&&i.indexOf(",")>-1?i.split(","):i},A="utf8=%26%2310003%3B",D="utf8=%E2%9C%93",H=function(e,r){var n={},c=r.ignoreQueryPrefix?e.replace(/^\?/,""):e,u=r.parameterLimit===1/0?void 0:r.parameterLimit,t=c.split(r.delimiter,u),l=-1,a,f=r.charset;if(r.charsetSentinel)for(a=0;a<t.length;++a)t[a].indexOf("utf8=")===0&&(t[a]===D?f="utf-8":t[a]===A&&(f="iso-8859-1"),l=a,a=t.length);for(a=0;a<t.length;++a)if(a!==l){var d=t[a],p=d.indexOf("]="),h=p===-1?d.indexOf("="):p+1,y,m;h===-1?(y=r.decoder(d,s.decoder,f,"key"),m=r.strictNullHandling?null:""):(y=r.decoder(d.slice(0,h),s.decoder,f,"key"),m=o.maybeMap(v(d.slice(h+1),r),function(w){return r.decoder(w,s.decoder,f,"value")})),m&&r.interpretNumericEntities&&f==="iso-8859-1"&&(m=E(m)),d.indexOf("[]=")>-1&&(m=x(m)?[m]:m),g.call(n,y)?n[y]=o.combine(n[y],m):n[y]=m}return n},C=function(i,e,r,n){for(var c=n?e:v(e,r),u=i.length-1;u>=0;--u){var t,l=i[u];if(l==="[]"&&r.parseArrays)t=[].concat(c);else{t=r.plainObjects?Object.create(null):{};var a=l.charAt(0)==="["&&l.charAt(l.length-1)==="]"?l.slice(1,-1):l,f=parseInt(a,10);!r.parseArrays&&a===""?t={0:c}:!isNaN(f)&&l!==a&&String(f)===a&&f>=0&&r.parseArrays&&f<=r.arrayLimit?(t=[],t[f]=c):a!=="__proto__"&&(t[a]=c)}c=t}return c},Q=function(e,r,n,c){if(!!e){var u=n.allowDots?e.replace(/\.([^.[]+)/g,"[$1]"):e,t=/(\[[^[\]]*])/,l=/(\[[^[\]]*])/g,a=n.depth>0&&t.exec(u),f=a?u.slice(0,a.index):u,d=[];if(f){if(!n.plainObjects&&g.call(Object.prototype,f)&&!n.allowPrototypes)return;d.push(f)}for(var p=0;n.depth>0&&(a=l.exec(u))!==null&&p<n.depth;){if(p+=1,!n.plainObjects&&g.call(Object.prototype,a[1].slice(1,-1))&&!n.allowPrototypes)return;d.push(a[1])}return a&&d.push("["+u.slice(a.index)+"]"),C(d,r,n,c)}},T=function(e){if(!e)return s;if(e.decoder!==null&&e.decoder!==void 0&&typeof e.decoder!="function")throw new TypeError("Decoder has to be a function.");if(typeof e.charset<"u"&&e.charset!=="utf-8"&&e.charset!=="iso-8859-1")throw new TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");var r=typeof e.charset>"u"?s.charset:e.charset;return{allowDots:typeof e.allowDots>"u"?s.allowDots:!!e.allowDots,allowPrototypes:typeof e.allowPrototypes=="boolean"?e.allowPrototypes:s.allowPrototypes,allowSparse:typeof e.allowSparse=="boolean"?e.allowSparse:s.allowSparse,arrayLimit:typeof e.arrayLimit=="number"?e.arrayLimit:s.arrayLimit,charset:r,charsetSentinel:typeof e.charsetSentinel=="boolean"?e.charsetSentinel:s.charsetSentinel,comma:typeof e.comma=="boolean"?e.comma:s.comma,decoder:typeof e.decoder=="function"?e.decoder:s.decoder,delimiter:typeof e.delimiter=="string"||o.isRegExp(e.delimiter)?e.delimiter:s.delimiter,depth:typeof e.depth=="number"||e.depth===!1?+e.depth:s.depth,ignoreQueryPrefix:e.ignoreQueryPrefix===!0,interpretNumericEntities:typeof e.interpretNumericEntities=="boolean"?e.interpretNumericEntities:s.interpretNumericEntities,parameterLimit:typeof e.parameterLimit=="number"?e.parameterLimit:s.parameterLimit,parseArrays:e.parseArrays!==!1,plainObjects:typeof e.plainObjects=="boolean"?e.plainObjects:s.plainObjects,strictNullHandling:typeof e.strictNullHandling=="boolean"?e.strictNullHandling:s.strictNullHandling}},_=function(i,e){var r=T(e);if(i===""||i===null||typeof i>"u")return r.plainObjects?Object.create(null):{};for(var n=typeof i=="string"?H(i,r):i,c=r.plainObjects?Object.create(null):{},u=Object.keys(n),t=0;t<u.length;++t){var l=u[t],a=Q(l,n[l],r,typeof i=="string");c=o.merge(c,a,r)}return r.allowSparse===!0?c:o.compact(c)},$=S,I=_,U=P,V={formats:U,parse:I,stringify:$};async function M({url:i,method:e="POST",params:r,useQS:n=!1,callback:c}){if(!i)return!1;const u=N.service({lock:!0,text:"\u8BF7\u7A0D\u5019",background:"rgba(0, 0, 0, 0.7)"});let t={baseURL:j.backendHost,url:i,method:e,withCredentials:!0};return e.toUpperCase()=="POST"?(t.headers={"content-type":"application/x-www-form-urlencoded"},t.data=n?V.stringify(r):r):e.toUpperCase()=="GET"&&(t.params=r),L(t).then(l=>{let a=l.data;if(!a.success)return b.error((a==null?void 0:a.msg)||"\u670D\u52A1\u5668\u9519\u8BEF"),null;let f=a.data;return typeof c=="function"&&c(f),f}).catch(l=>(console.error(l),b.error(l.message),!1)).finally(()=>{u.close()})}export{M as s};

View File

@ -0,0 +1 @@
import{m as r}from"./manage-list.8ab8e06b.js";import{s as t}from"./send_request.cc43fdb9.js";import{d as a,o as n,c as s,b as u}from"./index.f859bf4e.js";import"./el-overlay.1542ee54.js";import"./el-input.f35758e8.js";import"./el-progress.d53d438b.js";function i(e){return t({url:"/shop/good/manage/getCategoryList",method:"GET",params:e})}function o(e){return t({url:"/shop/good/manage/editCategory",method:"POST",useQS:!0,params:e})}function c(e){return t({url:"/shop/good/manage/deleteCategory",method:"POST",useQS:!0,params:e})}function d(e){return t({url:"/shop/good/manage/exportCategoryList",method:"GET",params:e})}const m={class:"container"},L=a({__name:"shop-cate-setting",setup(e){return(p,f)=>(n(),s("div",m,[u(r,{"list-func":i,"add-func":o,"edit-func":o,"delete-func":c,"export-func":d,"edit-permiss":"shop-cate-setting"},null,8,["list-func","add-func","edit-func","delete-func","export-func"])]))}});export{L as default};

View File

@ -0,0 +1 @@
import{m as n}from"./manage-list.8ab8e06b.js";import{s as e}from"./send_request.cc43fdb9.js";import{d as r,o as s,c as d,b as a}from"./index.f859bf4e.js";import"./el-overlay.1542ee54.js";import"./el-input.f35758e8.js";import"./el-progress.d53d438b.js";function u(o){return e({url:"/shop/good/manage/getGoodList",method:"GET",params:o})}function t(o){return e({url:"/shop/good/manage/editGood",method:"POST",useQS:!0,params:o})}function i(o){return e({url:"/shop/good/manage/deleteGood",method:"POST",useQS:!0,params:o})}function m(o){return e({url:"/shop/good/manage/exportGoodList",method:"GET",params:o})}const c={class:"container"},L=r({__name:"shop-good-setting",setup(o){return(p,f)=>(s(),d("div",c,[a(n,{"list-func":u,"add-func":t,"edit-func":t,"delete-func":i,"export-func":m,"edit-permiss":"shop-good-setting"},null,8,["list-func","add-func","edit-func","delete-func","export-func"])]))}});export{L as default};

View File

@ -0,0 +1 @@
import{s as r}from"./send_request.cc43fdb9.js";function u({username:e,password:s}){return r({url:"/user/login",method:"POST",useQS:!0,params:{username:e,password:s}})}function a(e){return r({url:"/user/manage/getUserList",method:"GET",params:e})}function n(e){return r({url:"/user/manage/editUser",method:"POST",useQS:!0,params:e})}function o(e){return r({url:"/user/manage/deleteUser",method:"POST",useQS:!0,params:e})}function m(e){return r({url:"/user/manage/exportUserList",method:"GET",params:e})}export{m as a,o as d,n as e,a as g,u};

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,9 @@
/*!
* Cropper.js v1.5.13
* https://fengyuanchen.github.io/cropperjs
*
* Copyright 2015-present Chen Fengyuan
* Released under the MIT license
*
* Date: 2022-11-20T05:30:43.444Z
*/.cropper-container{direction:ltr;font-size:0;line-height:0;position:relative;-ms-touch-action:none;touch-action:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.cropper-container img{-webkit-backface-visibility:hidden;backface-visibility:hidden;display:block;height:100%;image-orientation:0deg;max-height:none!important;max-width:none!important;min-height:0!important;min-width:0!important;width:100%}.cropper-wrap-box,.cropper-canvas,.cropper-drag-box,.cropper-crop-box,.cropper-modal{bottom:0;left:0;position:absolute;right:0;top:0}.cropper-wrap-box,.cropper-canvas{overflow:hidden}.cropper-drag-box{background-color:#fff;opacity:0}.cropper-modal{background-color:#000;opacity:.5}.cropper-view-box{display:block;height:100%;outline:1px solid #39f;outline-color:#3399ffbf;overflow:hidden;width:100%}.cropper-dashed{border:0 dashed #eee;display:block;opacity:.5;position:absolute}.cropper-dashed.dashed-h{border-bottom-width:1px;border-top-width:1px;height:calc(100% / 3);left:0;top:calc(100% / 3);width:100%}.cropper-dashed.dashed-v{border-left-width:1px;border-right-width:1px;height:100%;left:calc(100% / 3);top:0;width:calc(100% / 3)}.cropper-center{display:block;height:0;left:50%;opacity:.75;position:absolute;top:50%;width:0}.cropper-center:before,.cropper-center:after{background-color:#eee;content:" ";display:block;position:absolute}.cropper-center:before{height:1px;left:-3px;top:0;width:7px}.cropper-center:after{height:7px;left:0;top:-3px;width:1px}.cropper-face,.cropper-line,.cropper-point{display:block;height:100%;opacity:.1;position:absolute;width:100%}.cropper-face{background-color:#fff;left:0;top:0}.cropper-line{background-color:#39f}.cropper-line.line-e{cursor:ew-resize;right:-3px;top:0;width:5px}.cropper-line.line-n{cursor:ns-resize;height:5px;left:0;top:-3px}.cropper-line.line-w{cursor:ew-resize;left:-3px;top:0;width:5px}.cropper-line.line-s{bottom:-3px;cursor:ns-resize;height:5px;left:0}.cropper-point{background-color:#39f;height:5px;opacity:.75;width:5px}.cropper-point.point-e{cursor:ew-resize;margin-top:-3px;right:-3px;top:50%}.cropper-point.point-n{cursor:ns-resize;left:50%;margin-left:-3px;top:-3px}.cropper-point.point-w{cursor:ew-resize;left:-3px;margin-top:-3px;top:50%}.cropper-point.point-s{bottom:-3px;cursor:s-resize;left:50%;margin-left:-3px}.cropper-point.point-ne{cursor:nesw-resize;right:-3px;top:-3px}.cropper-point.point-nw{cursor:nwse-resize;left:-3px;top:-3px}.cropper-point.point-sw{bottom:-3px;cursor:nesw-resize;left:-3px}.cropper-point.point-se{bottom:-3px;cursor:nwse-resize;height:20px;opacity:1;right:-3px;width:20px}@media (min-width: 768px){.cropper-point.point-se{height:15px;width:15px}}@media (min-width: 992px){.cropper-point.point-se{height:10px;width:10px}}@media (min-width: 1200px){.cropper-point.point-se{height:5px;opacity:.75;width:5px}}.cropper-point.point-se:before{background-color:#39f;bottom:-50%;content:" ";display:block;height:200%;opacity:0;position:absolute;right:-50%;width:200%}.cropper-invisible{opacity:0}.cropper-bg{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC)}.cropper-hide{display:block;height:0;position:absolute;width:0}.cropper-hidden{display:none!important}.cropper-move{cursor:move}.cropper-crop{cursor:crosshair}.cropper-disabled .cropper-drag-box,.cropper-disabled .cropper-face,.cropper-disabled .cropper-line,.cropper-disabled .cropper-point{cursor:not-allowed}.info[data-v-cdea44c0]{text-align:center;padding:35px 0}.info-image[data-v-cdea44c0]{position:relative;margin:auto;width:100px;height:100px;background:#f8f8f8;border:1px solid #eee;border-radius:50px;overflow:hidden}.info-edit[data-v-cdea44c0]{display:flex;justify-content:center;align-items:center;position:absolute;left:0;top:0;width:100%;height:100%;background:rgba(0,0,0,.5);opacity:0;transition:opacity .3s ease}.info-edit i[data-v-cdea44c0]{color:#eee;font-size:25px}.info-image:hover .info-edit[data-v-cdea44c0]{opacity:1}.info-name[data-v-cdea44c0]{margin:15px 0 10px;font-size:24px;font-weight:500;color:#262626}.crop-demo-btn[data-v-cdea44c0]{position:relative}.crop-input[data-v-cdea44c0]{position:absolute;width:100px;height:40px;left:0;top:0;opacity:0;cursor:pointer}

View File

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title></title>
<link rel="stylesheet" href="https://at.alicdn.com/t/font_830376_qzecyukz0s.css">
<script type="module" crossorigin src="./assets/index.f859bf4e.js"></script>
<link rel="stylesheet" href="./assets/index.87e2e9b7.css">
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

View File

@ -61,6 +61,13 @@ public class AccessLogController {
return Res.error("参数错误"); return Res.error("参数错误");
} }
// 推送到门禁端
JSONObject websocketData = new JSONObject();
websocketData.put("gateId", gateId);
websocketData.put("action", "onopen");
String websocketJSON = websocketData.toString();
WebSocketServer.sendAllMessage(websocketJSON);
AccessLog accessLog = new AccessLog(); AccessLog accessLog = new AccessLog();
accessLog.setId(null); accessLog.setId(null);
accessLog.setType(type); accessLog.setType(type);

View File

@ -58,6 +58,14 @@ public class GateController {
Long gateId = Long.valueOf(id); Long gateId = Long.valueOf(id);
Gate gate = gateService.getGateById(gateId); Gate gate = gateService.getGateById(gateId);
GateVO gateVO = GateVO.convertFrom(gate); GateVO gateVO = GateVO.convertFrom(gate);
// 推送到门禁端
JSONObject websocketData = new JSONObject();
websocketData.put("gateId", id);
websocketData.put("action", "onscan");
String websocketJSON = websocketData.toString();
WebSocketServer.sendAllMessage(websocketJSON);
return Res.success(gateVO); return Res.success(gateVO);
} }

View File

@ -1,5 +1,6 @@
package com.cxyxiaomo.epp.access.controller; package com.cxyxiaomo.epp.access.controller;
import com.alibaba.fastjson2.JSONObject;
import com.cxyxiaomo.epp.access.pojo.UnlimitedQRCodeParam; import com.cxyxiaomo.epp.access.pojo.UnlimitedQRCodeParam;
import com.cxyxiaomo.epp.access.service.WeChatTokenServiceImpl; import com.cxyxiaomo.epp.access.service.WeChatTokenServiceImpl;
import com.cxyxiaomo.epp.common.response.Res; import com.cxyxiaomo.epp.common.response.Res;
@ -47,7 +48,28 @@ public class WeChatTokenController {
unlimitedQRCodeParam.setIsHyaline(isHyaline); unlimitedQRCodeParam.setIsHyaline(isHyaline);
okhttp3.ResponseBody responseBody = weChatTokenService.getUnlimitedQRCodeFromApi(accessToken, unlimitedQRCodeParam); okhttp3.ResponseBody responseBody = weChatTokenService.getUnlimitedQRCodeFromApi(accessToken, unlimitedQRCodeParam);
return responseBody.bytes(); try {
// {"errcode":40001,"errmsg":"invalid credential, access_token is invalid or not latest, could get access_token by getStableAccessToken, more details at https://mmbizurl.cn/s/JtxxFh33r rid: 6446bac4-4a6b0410-118a16e7"}
if (Objects.requireNonNull(responseBody.contentType()).subtype().equals("json")) {
// 返回了 JSON 说明失败了
String jsonString = responseBody.string();
JSONObject jsonObject = JSONObject.parseObject(jsonString);
String errcode = jsonObject.getString("errcode");
if (errcode.equals("40001")) {
// 重新获取 Access Token
accessToken = weChatTokenService.getAccessToken(true);
responseBody = weChatTokenService.getUnlimitedQRCodeFromApi(accessToken, unlimitedQRCodeParam);
} else if (errcode.equals("40013")) {
System.out.println("40013 invalid appid 不合法的 AppID ,请开发者检查 AppID 的正确性,避免异常字符,注意大小写\n" +
"docs: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html");
return null;
}
}
// 返回了图片直接返回
return responseBody.bytes();
} catch (NullPointerException ignored) {
return null;
}
} }
@GetMapping(value = "/rpc/getOpenIdFromApi") @GetMapping(value = "/rpc/getOpenIdFromApi")

View File

@ -18,7 +18,7 @@ public class WebSocketServer {
private Session session; private Session session;
// session集合,存放对应的session // session集合,存放对应的session
private static ConcurrentHashMap<Integer, Session> sessionPool = new ConcurrentHashMap<>(); private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();
// concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象 // concurrent包的线程安全Set,用来存放每个客户端对应的WebSocket对象
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>(); private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
@ -30,7 +30,7 @@ public class WebSocketServer {
* @param userId 用户ID * @param userId 用户ID
*/ */
@OnOpen @OnOpen
public void onOpen(Session session, @PathParam(value = "userId") Integer userId) { public void onOpen(Session session, @PathParam(value = "userId") String userId) {
log.info("WebSocket建立连接中,连接用户ID{}", userId); log.info("WebSocket建立连接中,连接用户ID{}", userId);
try { try {
Session historySession = sessionPool.get(userId); Session historySession = sessionPool.get(userId);
@ -84,7 +84,7 @@ public class WebSocketServer {
* @param userId 用户ID * @param userId 用户ID
* @param message 发送的消息 * @param message 发送的消息
*/ */
public static void sendMessageByUser(Integer userId, String message) { public static void sendMessageByUser(String userId, String message) {
log.info("用户ID" + userId + ",推送内容:" + message); log.info("用户ID" + userId + ",推送内容:" + message);
Session session = sessionPool.get(userId); Session session = sessionPool.get(userId);
try { try {

View File

@ -35,11 +35,17 @@ public class WeChatTokenServiceImpl implements WeChatTokenService {
@Override @Override
public String getAccessToken() { public String getAccessToken() {
// 首先从数据库中查询是否存在 access_token return getAccessToken(false);
// 如果存在且没有过期那么就直接返回距离失效时间小于 3 分钟就当作过期 }
Setting atSetting = accessDao.getValueByKey(SETTING_KEY);
if (atSetting != null && LocalDateTime.now().plusMinutes(3L).compareTo(atSetting.getTime()) < 0) { public String getAccessToken(Boolean forceUpdate) {
return atSetting.getValue(); if (!forceUpdate) {
// 首先从数据库中查询是否存在 access_token
// 如果存在且没有过期那么就直接返回距离失效时间小于 3 分钟就当作过期
Setting atSetting = accessDao.getValueByKey(SETTING_KEY);
if (atSetting != null && LocalDateTime.now().plusMinutes(3L).compareTo(atSetting.getTime()) < 0) {
return atSetting.getValue();
}
} }
// 否则则去请求一个新的 access_token // 否则则去请求一个新的 access_token

View File

@ -2,18 +2,31 @@ package com.cxyxiaomo.epp.shop.controller;
import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.cxyxiaomo.epp.PageTable.enums.AddType;
import com.cxyxiaomo.epp.PageTable.enums.EditType;
import com.cxyxiaomo.epp.PageTable.enums.FieldType;
import com.cxyxiaomo.epp.PageTable.enums.SearchType;
import com.cxyxiaomo.epp.PageTable.query.PageQuery;
import com.cxyxiaomo.epp.PageTable.utils.FieldBuilder;
import com.cxyxiaomo.epp.PageTable.utils.FieldMapperBuilder;
import com.cxyxiaomo.epp.PageTable.utils.FieldRuleBuilder;
import com.cxyxiaomo.epp.PageTable.utils.FieldRuleListBuilder;
import com.cxyxiaomo.epp.common.enums.OrderStatus; import com.cxyxiaomo.epp.common.enums.OrderStatus;
import com.cxyxiaomo.epp.common.pojo.Order; import com.cxyxiaomo.epp.common.pojo.Order;
import com.cxyxiaomo.epp.common.pojo.OrderDetail; import com.cxyxiaomo.epp.common.pojo.OrderDetail;
import com.cxyxiaomo.epp.common.query.OrderQuery;
import com.cxyxiaomo.epp.common.response.Res; import com.cxyxiaomo.epp.common.response.Res;
import com.cxyxiaomo.epp.common.vo.GoodVO; import com.cxyxiaomo.epp.common.vo.GoodVO;
import com.cxyxiaomo.epp.common.vo.OrderDetailVO; import com.cxyxiaomo.epp.common.vo.OrderDetailVO;
import com.cxyxiaomo.epp.common.vo.OrderVO; import com.cxyxiaomo.epp.common.vo.OrderVO;
import com.cxyxiaomo.epp.shop.service.GoodService; import com.cxyxiaomo.epp.shop.service.GoodService;
import com.cxyxiaomo.epp.shop.service.OrderService; import com.cxyxiaomo.epp.shop.service.OrderService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -30,8 +43,8 @@ public class OrderController {
/** /**
* 小程序端创建订单 * 小程序端创建订单
* *
* @param userId 下单用户 * @param params userId 下单用户
* @param orderList 下单商品 [ { goodId, count }, ... ] * @param params orderList 下单商品 [ { goodId, count }, ... ]
* @return * @return
*/ */
@PostMapping("/miniprogram/createOrder") @PostMapping("/miniprogram/createOrder")
@ -184,6 +197,12 @@ public class OrderController {
return Res.success(success ? "支付成功" : "支付失败"); return Res.success(success ? "支付成功" : "支付失败");
} }
/**
* 用户取消订单
*
* @param params
* @return
*/
@PostMapping("/miniprogram/cancelOrder") @PostMapping("/miniprogram/cancelOrder")
@ResponseBody @ResponseBody
public Res cancelOrder(@RequestBody JSONObject params) { public Res cancelOrder(@RequestBody JSONObject params) {
@ -224,6 +243,106 @@ public class OrderController {
return Res.success(success ? "取消成功" : "取消失败"); return Res.success(success ? "取消成功" : "取消失败");
} }
/**
* 订单发货
*
* @param orderId
* @param expressId
* @param comment
* @return
*/
@PostMapping("/manage/deliverOrder")
@ResponseBody
public Res deliverOrder(Long orderId, String expressId, String comment) {
if (orderId == null || expressId == null || comment == null) {
return Res.error("参数错误");
}
if (expressId.equals("") && comment.equals("")) {
return Res.error("运单号 和 发货备注 不可同时为空");
}
// 查询订单详情
Order order = orderService.getOrderById(orderId);
if (order == null) {
return Res.error("订单不存在");
}
//判断是否可以发货
OrderStatus orderStatus = OrderStatus.get(order.getOrderStatus());
switch (orderStatus) {
case PENDING:
return Res.error("订单尚未支付,不可发货");
case PROCESSING: // 订单未发货
// 更新订单发货信息
orderService.updateOrderShipInfo(orderId, expressId, comment);
// 更新支付信息
orderService.updateOrderStatus(orderId, OrderStatus.SHIPPED);
return Res.success("发货成功");
case SHIPPED: // 订单已发货修改发货信息
// 更新订单发货信息
orderService.updateOrderShipInfo(orderId, expressId, comment);
return Res.success("发货信息修改成功");
case DELIVERED:
return Res.error("订单已完成,无法再次发货");
case CANCELLED:
return Res.error("订单已取消,不可发货");
default:
return Res.error("当前订单状态不可发货");
}
}
/**
* 管理员取消订单
*
* @param orderId
* @return
*/
@PostMapping("/manage/cancelOrder")
@ResponseBody
public Res cancelOrderByManager(Long orderId) {
if (orderId == null) {
return Res.error("参数错误");
}
// 查询订单详情
Order order = orderService.getOrderById(orderId);
if (order == null) {
return Res.error("订单不存在");
}
//判断是否可以取消
OrderStatus orderStatus = OrderStatus.get(order.getOrderStatus());
switch (orderStatus) {
case PENDING:
// 更新订单发货信息
orderService.updateOrderShipInfo(orderId, "", "管理员取消了您的订单,如有疑问请联系管理员");
// 更新支付信息
orderService.updateOrderStatus(orderId, OrderStatus.CANCELLED);
return Res.success("操作成功");
case PROCESSING:
// 更新订单发货信息
orderService.updateOrderShipInfo(orderId, "", "管理员取消了您的订单,费用已退回至您的帐户,如有疑问请联系管理员");
// 更新支付信息
orderService.updateOrderStatus(orderId, OrderStatus.CANCELLED);
return Res.success("操作成功");
case SHIPPED:
return Res.error("订单已发货,不可取消");
case DELIVERED:
return Res.error("订单已送达,不可取消");
case CANCELLED:
return Res.error("订单已取消");
default:
return Res.error("当前订单状态无法取消");
}
}
/**
* 订单确认收货
*
* @param params
* @return
*/
@PostMapping("/miniprogram/confirmOrder") @PostMapping("/miniprogram/confirmOrder")
@ResponseBody @ResponseBody
public Res confirmOrder(@RequestBody JSONObject params) { public Res confirmOrder(@RequestBody JSONObject params) {
@ -261,4 +380,189 @@ public class OrderController {
return Res.success(success ? "确认收货成功" : "确认收货失败"); return Res.success(success ? "确认收货成功" : "确认收货失败");
} }
/**
* 获取订单列表
*
* @return
*/
@GetMapping("/manage/getOrderList")
@ResponseBody
public Res getOrderList(PageQuery pageQuery, OrderQuery orderQuery) {
// 查询分页数据
PageHelper.startPage(pageQuery.getPageIndex(), pageQuery.getPageSize());
List<Order> orderList = orderService.getOrderList(orderQuery);
PageInfo<Order> orderPageInfo = new PageInfo<>(orderList);
List<Order> list = orderPageInfo.getList();
List<OrderVO> voList = OrderVO.convertFrom(list);
// id列 字段名区分大小写以VO中的变量名为准
// 新增修改弹窗时使用该列作为主键列进行操作
String idFieldName = "id";
// 当前管理页面
String pageName = "订单管理";
// 指定前端表格显示列
JSONArray columns = FieldBuilder.create()
.add("userId", "userId", "用户ID", "",
FieldType.TEXT, SearchType.INPUT, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.add("orderStatusCode", "orderStatusName", "订单状态", true,
FieldType.TEXT, SearchType.SELECT, AddType.CAN_NOT_ADD, EditType.SELECT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, "订单状态",
FieldRuleListBuilder.create()
.add(FieldRuleBuilder.create("订单状态").required()),
null
)
.add("orderPrice", "orderPrice", "订单价格", "",
FieldType.TEXT, SearchType.CAN_NOT_SEARCH, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.add("orderDate", "orderDate", "下单时间", "",
FieldType.TEXT, SearchType.CAN_NOT_SEARCH, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.add("payDate", "payDate", "支付时间", "",
FieldType.TEXT, SearchType.CAN_NOT_SEARCH, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.add("cancelDate", "cancelDate", "取消时间", "",
FieldType.TEXT, SearchType.CAN_NOT_SEARCH, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.add("shipDate", "shipDate", "发货时间", "",
FieldType.TEXT, SearchType.CAN_NOT_SEARCH, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.add("deliverDate", "deliverDate", "送达时间", "",
FieldType.TEXT, SearchType.CAN_NOT_SEARCH, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.add("expressId", "expressId", "运单号", "",
FieldType.TEXT, SearchType.INPUT, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.add("comment", "comment", "发货备注", "",
FieldType.LONG_TEXT, SearchType.CAN_NOT_SEARCH, AddType.CAN_NOT_ADD, EditType.CAN_NOT_EDIT,
FieldBuilder.SEARCH_PLACEHOLDER_SAME_AS_FIELDNAME,
null, null, null, null
)
.build();
// 指定需要翻译的字段
OrderStatus[] orderStatusList = OrderStatus.values();
HashMap<String, String> orderStatusMap = new HashMap<>(orderStatusList.length);
for (OrderStatus orderStatus : orderStatusList) {
orderStatusMap.put(orderStatus.getValue(), orderStatus.toString());
}
// build
JSONArray fieldMapper = FieldMapperBuilder.create()
.add("orderStatusCode", "orderStatusName", orderStatusMap)
.build();
// 拼装返回结果
JSONObject map = new JSONObject(6);
map.put("total", orderPageInfo.getTotal());
map.put("list", voList);
map.put("columns", columns);
map.put("fieldMapper", fieldMapper);
map.put("idFieldName", idFieldName);
map.put("pageName", pageName);
// 返回结果
return Res.success(map);
}
// /**
// * 编辑订单
// *
// * @return
// */
// @PostMapping("/manage/editOrder")
// @ResponseBody
// public Res editOrder(@ModelAttribute OrderVO OrderVO) {
// Order Order = OrderVO.convertTo(OrderVO);
//
// // 先查询订单是否存在
// Order existOrder = orderService.getOrderById(Order.getId());
//
// if (Order.getId() == null || Order.getId() < 1) {
// // 新增订单
// if (existOrder != null) {
// return Res.error("订单已存在,操作失败");
// }
//
// if (Order.getOrdersName() == null || "".equals(Order.getOrdersName())) {
// return Res.error("订单名称不能为空");
// }
// Order.setId(null);
// orderService.addOrder(Order);
// } else {
// // 修改订单
// if (existOrder == null) {
// return Res.error("订单不存在,操作失败");
// }
//
// orderService.updateOrder(Order);
// }
// return Res.success(true);
// }
//
// /**
// * 关闭订单
// *
// * @param id
// * @return
// */
// @PostMapping("/manage/deleteOrder")
// @ResponseBody
// public Res deleteOrder(Long id) {
// if (id == null || id <= 0) {
// return Res.error("订单不存在,删除失败");
// }
// // 先查询订单是否存在
// Order existOrder = orderService.getOrderById(id);
// if (existOrder == null) {
// return Res.error("订单不存在,删除失败");
// }
// boolean b = orderService.deleteOrder(existOrder.getId());
// return Res.success(b);
// }
/**
* 导出订单列表
*
* @return
*/
@GetMapping("/manage/exportOrderList")
@ResponseBody
public Res exportOrderList(OrderQuery orderQuery) {
List<Order> orderList = orderService.getOrderList(orderQuery);
List<OrderVO> orderVOList = OrderVO.convertFrom(orderList);
// 当前时间
Date now = Calendar.getInstance().getTime();
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd_HHmmss");
String dateTime = format.format(now);
HashMap<String, Object> map = new HashMap<>();
map.put("list", orderVOList);
map.put("sheetName", "订单表-" + System.currentTimeMillis());
map.put("fileName", "订单表_导出时间_" + dateTime);
return Res.success(map);
}
} }

View File

@ -27,13 +27,7 @@ public interface GoodDao {
Integer deleteById(Integer id); Integer deleteById(Integer id);
// Manage
public boolean addGood(Good good); public boolean addGood(Good good);
public boolean updateGood(Good good); public boolean updateGood(Good good);

View File

@ -2,6 +2,7 @@ package com.cxyxiaomo.epp.shop.dao;
import com.cxyxiaomo.epp.common.pojo.Order; import com.cxyxiaomo.epp.common.pojo.Order;
import com.cxyxiaomo.epp.common.pojo.OrderDetail; import com.cxyxiaomo.epp.common.pojo.OrderDetail;
import com.cxyxiaomo.epp.common.query.OrderQuery;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -62,6 +63,19 @@ public interface OrderDao {
*/ */
int updateOrderStatus(@Param("orderId") Long orderId, @Param("orderStatus") String orderStatus); int updateOrderStatus(@Param("orderId") Long orderId, @Param("orderStatus") String orderStatus);
/**
* 更新订单发货信息
*
* @param orderId
* @param expressId
* @param comment
* @return
*/
int updateOrderShipInfo(@Param("orderId") Long orderId, @Param("expressId") String expressId, @Param("comment") String comment);
// 根据订单 ID 删除订单信息及订单详情信息 // 根据订单 ID 删除订单信息及订单详情信息
int deleteOrderById(Long orderId); boolean deleteOrderById(Long orderId);
public List<Order> getOrderList(OrderQuery orderQuery);
} }

View File

@ -4,6 +4,7 @@ import com.cxyxiaomo.epp.common.enums.OrderStatus;
import com.cxyxiaomo.epp.common.pojo.Good; import com.cxyxiaomo.epp.common.pojo.Good;
import com.cxyxiaomo.epp.common.pojo.Order; import com.cxyxiaomo.epp.common.pojo.Order;
import com.cxyxiaomo.epp.common.pojo.OrderDetail; import com.cxyxiaomo.epp.common.pojo.OrderDetail;
import com.cxyxiaomo.epp.common.query.OrderQuery;
import com.cxyxiaomo.epp.common.utils.SnowflakeManager; import com.cxyxiaomo.epp.common.utils.SnowflakeManager;
import com.cxyxiaomo.epp.common.vo.OrderDetailVO; import com.cxyxiaomo.epp.common.vo.OrderDetailVO;
import com.cxyxiaomo.epp.shop.dao.GoodDao; import com.cxyxiaomo.epp.shop.dao.GoodDao;
@ -132,4 +133,15 @@ public class OrderService {
int affectRows = orderDao.updateOrderStatus(orderId, orderStatus.getValue()); int affectRows = orderDao.updateOrderStatus(orderId, orderStatus.getValue());
return affectRows > 0; return affectRows > 0;
} }
public Boolean updateOrderShipInfo(Long orderId, String expressId, String comment) {
int affectRows = orderDao.updateOrderShipInfo(orderId, expressId, comment);
return affectRows > 0;
}
public List<Order> getOrderList(OrderQuery orderQuery) {
return orderDao.getOrderList(orderQuery);
}
} }

View File

@ -95,7 +95,7 @@
</update> </update>
<!-- 根据订单 ID 删除订单信息及订单详情信息 --> <!-- 根据订单 ID 删除订单信息及订单详情信息 -->
<delete id="deleteOrderById" parameterType="java.lang.Integer"> <delete id="deleteOrderById" parameterType="java.lang.Long">
DELETE DELETE
FROM `order` FROM `order`
WHERE id = #{orderId}; WHERE id = #{orderId};
@ -104,4 +104,39 @@
-- FROM goods_order_details -- FROM goods_order_details
-- WHERE order_id = #{orderId}; -- WHERE order_id = #{orderId};
</delete> </delete>
<!-- manage 相关的 SQL -->
<select id="getOrderList" resultType="com.cxyxiaomo.epp.common.pojo.Order"
parameterType="com.cxyxiaomo.epp.common.query.OrderQuery">
SELECT * FROM `order`
<where>
<if test="id != null">
AND id = #{id}
</if>
<if test="userId != null">
AND user_id = #{userId}
</if>
<if test="orderStatusCode != null and orderStatusCode != ''">
AND order_status = #{orderStatusCode}
</if>
<if test="expressId != null and expressId != ''">
AND express_id = #{expressId}
</if>
</where>
</select>
<!-- 更新发货信息 -->
<update id="updateOrderShipInfo" parameterType="com.cxyxiaomo.epp.common.pojo.Order">
UPDATE `order`
<set>
<if test="expressId != null">
express_id = #{expressId},
</if>
<if test="comment != null">
`comment` = #{comment},
</if>
</set>
WHERE id = #{orderId}
</update>
</mapper> </mapper>

View File

@ -11,6 +11,13 @@
</head> </head>
<body> <body>
<!--
FIXME 环境配置
src
- 线上环境:"https://epp.only4.work/access/index.html?inElectron=true"
- 开发环境:"http://localhost/access/index.html?inElectron=true"
-->
<iframe src="https://epp-prod.only4.work/access/index.html?inElectron=true" frameborder="0"></iframe> <iframe src="https://epp-prod.only4.work/access/index.html?inElectron=true" frameborder="0"></iframe>
<script src="./renderer.js" type="module"></script> <script src="./renderer.js" type="module"></script>
</body> </body>

View File

@ -7,13 +7,10 @@ export {}
declare module '@vue/runtime-core' { declare module '@vue/runtime-core' {
export interface GlobalComponents { export interface GlobalComponents {
Calender: typeof import('./src/components/calender.vue')['default']
ContextMenu: typeof import('./src/components/context-menu.vue')['default']
ElAlert: typeof import('element-plus/es')['ElAlert'] ElAlert: typeof import('element-plus/es')['ElAlert']
ElAvatar: typeof import('element-plus/es')['ElAvatar'] ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElButton: typeof import('element-plus/es')['ElButton'] ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard'] ElCard: typeof import('element-plus/es')['ElCard']
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
ElCol: typeof import('element-plus/es')['ElCol'] ElCol: typeof import('element-plus/es')['ElCol']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog'] ElDialog: typeof import('element-plus/es')['ElDialog']
@ -38,6 +35,7 @@ declare module '@vue/runtime-core' {
ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElTable: typeof import('element-plus/es')['ElTable'] ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
ElTooltip: typeof import('element-plus/es')['ElTooltip'] ElTooltip: typeof import('element-plus/es')['ElTooltip']
ElUpload: typeof import('element-plus/es')['ElUpload'] ElUpload: typeof import('element-plus/es')['ElUpload']
Header: typeof import('./src/components/header.vue')['default'] Header: typeof import('./src/components/header.vue')['default']

View File

@ -24,6 +24,7 @@
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@types/mockjs": "^1.0.7",
"@vitejs/plugin-vue": "^3.0.0", "@vitejs/plugin-vue": "^3.0.0",
"@vue/compiler-sfc": "^3.1.2", "@vue/compiler-sfc": "^3.1.2",
"typescript": "^4.6.4", "typescript": "^4.6.4",
@ -223,6 +224,12 @@
"@types/lodash": "*" "@types/lodash": "*"
} }
}, },
"node_modules/@types/mockjs": {
"version": "1.0.7",
"resolved": "https://registry.npmmirror.com/@types/mockjs/-/mockjs-1.0.7.tgz",
"integrity": "sha512-OCxXz6hEaJOVpRwuJMiVY5a6LtJcih+br9gwB/Q8ooOBikvk5FpBQ31OlNimXo3EqKha1Z7PFBni+q9m+8NCWg==",
"dev": true
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "18.14.0", "version": "18.14.0",
"resolved": "https://registry.npmmirror.com/@types/node/-/node-18.14.0.tgz", "resolved": "https://registry.npmmirror.com/@types/node/-/node-18.14.0.tgz",

View File

@ -24,6 +24,7 @@
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"devDependencies": { "devDependencies": {
"@types/mockjs": "^1.0.7",
"@vitejs/plugin-vue": "^3.0.0", "@vitejs/plugin-vue": "^3.0.0",
"@vue/compiler-sfc": "^3.1.2", "@vue/compiler-sfc": "^3.1.2",
"typescript": "^4.6.4", "typescript": "^4.6.4",

View File

@ -0,0 +1,63 @@
import send_request from '../utils/send_request';
/**
* 获取订单列表
* @returns
*/
export function getOrderList(params) {
return send_request({
url: '/shop/order/manage/getOrderList',
method: 'GET',
params: params,
});
};
/**
* 获取订单详情
* @returns
*/
export function getOrderDetail(params) {
return send_request({
url: '/shop/order/miniprogram/orderDetail',
method: 'GET',
params: params,
});
};
/**
* 订单发货
* @returns
*/
export function deliverOrder(params) {
return send_request({
url: '/shop/order/manage/deliverOrder',
method: 'POST',
useQS: true,
params: params,
});
};
/**
* 关闭订单
* @returns
*/
export function withdrawOrder(params) {
return send_request({
url: '/shop/order/manage/cancelOrder',
method: 'POST',
useQS: true,
params: params,
});
};
/**
* 导出订单列表
* @returns
*/
export function exportOrderList(params) {
return send_request({
url: '/shop/order/manage/exportOrderList',
method: 'GET',
params: params,
});
};

View File

@ -24,6 +24,24 @@ export function userLogout() {
return null; return null;
}; };
/**
* 修改密码
* @returns
*/
export function updatePwd({ oldpwd, newpwd }) {
let userId = localStorage.getItem("ms_user_id")
return send_request({
url: '/user/updatePwd',
method: 'POST',
useQS: true,
params: {
"userId": userId,
"oldpwd": oldpwd,
"newpwd": newpwd,
},
});
};
/** /**
* 获取用户列表 * 获取用户列表
* @returns * @returns

View File

@ -13,7 +13,7 @@
<div class="header-right"> <div class="header-right">
<div class="header-user-con"> <div class="header-user-con">
<!-- 消息中心 --> <!-- 消息中心 -->
<div class="btn-bell" @click="router.push('/tabs')"> <div class="btn-bell" @click="router.push('/tabs')" v-if="false">
<el-tooltip effect="dark" :content="message ? `有${message}条未读消息` : `消息中心`" placement="bottom"> <el-tooltip effect="dark" :content="message ? `有${message}条未读消息` : `消息中心`" placement="bottom">
<i class="el-icon-lx-notice"></i> <i class="el-icon-lx-notice"></i>
</el-tooltip> </el-tooltip>

View File

@ -56,16 +56,27 @@
</el-tooltip> </el-tooltip>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="操作" width="220" align="center" v-if="props.editFunc || props.deleteFunc"> <el-table-column label="操作" width="220" align="center"
v-if="props.editFunc || props.deleteFunc || props.customEditHandle">
<template #default="scope"> <template #default="scope">
<el-button text :icon="Edit" @click="handleEdit(scope.$index, scope.row)" <template v-if="props.customEditHandle">
v-permiss="props.editPermiss" v-if="props.editFunc"> <el-button text :icon="List"
编辑 @click="props.customEditHandle((scope as any).$index, (scope as any).row, getData)"
</el-button> v-permiss="props.editPermiss">
<el-button text :icon="Delete" class="red" @click="handleDelete(scope.$index, scope.row)" 管理
v-permiss="props.editPermiss" v-if="props.deleteFunc"> </el-button>
删除 </template>
</el-button> <template v-else>
<el-button text :icon="Edit" @click="handleEdit((scope as any).$index, (scope as any).row)"
v-permiss="props.editPermiss" v-if="props.editFunc">
编辑
</el-button>
<el-button text :icon="Delete" class="red"
@click="handleDelete((scope as any).$index, (scope as any).row)"
v-permiss="props.editPermiss" v-if="props.deleteFunc">
删除
</el-button>
</template>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -141,7 +152,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue'; import { ref, reactive, onMounted, computed } from 'vue';
import { FormInstance, FormRules, ElMessage, ElMessageBox } from 'element-plus'; import { FormInstance, FormRules, ElMessage, ElMessageBox } from 'element-plus';
import { Delete, Edit, Search, Plus, Filter, Download } from '@element-plus/icons-vue'; import { Delete, Edit, Search, Plus, Filter, Download, List } from '@element-plus/icons-vue';
import * as xlsx from 'xlsx'; import * as xlsx from 'xlsx';
import Mock from 'mockjs'; import Mock from 'mockjs';
import ImageUpload from './image-upload.vue'; import ImageUpload from './image-upload.vue';
@ -176,6 +187,12 @@ const props = defineProps({
'editPermiss': { 'editPermiss': {
type: String, type: String,
required: true, required: true,
},
//
'customEditHandle': {
type: Function,
required: false,
} }
}) })
@ -431,10 +448,10 @@ const saveEdit = async (formEl: FormInstance | undefined) => {
var result; var result;
if (formId > 0) { if (formId > 0) {
// //
var result = await props.editFunc(form) var result = await props.editFunc?.(form)
} else { } else {
// //
var result = await props.addFunc(form) var result = await props.addFunc?.(form)
query.pageIndex = Math.ceil((pageTotal.value + 1) / query.pageSize); query.pageIndex = Math.ceil((pageTotal.value + 1) / query.pageSize);
} }
console.log("result", result) console.log("result", result)
@ -460,7 +477,7 @@ const handleDelete = (index: number, row: any) => {
// //
ElMessageBox.confirm('确定要删除吗?', '提示', { type: 'warning' }) ElMessageBox.confirm('确定要删除吗?', '提示', { type: 'warning' })
.then(async () => { .then(async () => {
var result = await props.deleteFunc({ var result = await props.deleteFunc?.({
id: row[idFieldName], id: row[idFieldName],
}) })
if (result) { if (result) {
@ -540,7 +557,7 @@ const handleExport = async () => {
// //
let excelList = dataList.map((row: any) => { let excelList = dataList.map((row: any) => {
// key // key
return fieldNameList.map((field: any) => String(row[field])) return fieldNameList.map((field: any) => String(row[field] || ""))
}) })
excelList.unshift(firstRow) // excelList.unshift(firstRow) //

View File

@ -91,6 +91,11 @@ const items = [
title: '商品管理', title: '商品管理',
permiss: 'shop-good-setting', permiss: 'shop-good-setting',
}, },
{
index: '/shop-order-setting',
title: '订单管理',
permiss: 'shop-order-setting',
},
], ],
}, },
{ {

View File

@ -84,6 +84,15 @@ const routes: RouteRecordRaw[] = [
}, },
component: () => import('../views/shop-good-setting.vue'), component: () => import('../views/shop-good-setting.vue'),
}, },
{
path: '/shop-order-setting',
name: 'shop-order-setting',
meta: {
title: '订单管理',
permiss: 'shop-order-setting',
},
component: () => import('../views/shop-order-setting.vue'),
},
{ {

View File

@ -25,6 +25,7 @@ export const usePermissStore = defineStore('permiss', {
"shop", "shop",
"shop-cate-setting", "shop-cate-setting",
"shop-good-setting", "shop-good-setting",
"shop-order-setting",
"privilege", "privilege",
"privilege-user-setting", "privilege-user-setting",
@ -46,6 +47,7 @@ export const usePermissStore = defineStore('permiss', {
"shop", "shop",
"shop-cate-setting", "shop-cate-setting",
"shop-good-setting", "shop-good-setting",
"shop-order-setting",
"privilege", "privilege",
"privilege-user-setting", "privilege-user-setting",

View File

@ -22,6 +22,12 @@ export default {
/** /**
* 后端接口请求地址 * 后端接口请求地址
* 结尾加不加 / 都可 * 结尾加不加 / 都可
*
* FIXME 环境配置
*
* backendHost
* - 线上环境"https://epp.only4.work/"
* - 开发环境"http://localhost/"
*/ */
backendHost: "https://epp-prod.only4.work/", backendHost: "https://epp-prod.only4.work/",
}; };

View File

@ -1,213 +1,34 @@
<template> <template>
<div class="container"> <div class="container">
<el-row :gutter="20"> <el-card shadow="hover" class="mgb20" style="height: 252px">
<el-col :span="8"> <div class="user-info">
<el-card shadow="hover" class="mgb20" style="height: 252px"> <el-avatar :size="120" :src="imgurl" />
<div class="user-info"> <div class="user-info-cont">
<el-avatar :size="120" :src="imgurl" /> <div class="user-info-name">{{ name }}</div>
<div class="user-info-cont"> <div>{{ role }}</div>
<div class="user-info-name">{{ name }}</div> </div>
<div>{{ role }}</div> </div>
</div> <div class="user-info-list">
</div> 上次登录时间
<div class="user-info-list"> <span>2022-10-01</span>
上次登录时间 </div>
<span>2022-10-01</span> <div class="user-info-list">
</div> 上次登录地点
<div class="user-info-list"> <span>东莞</span>
上次登录地点 </div>
<span>东莞</span> </el-card>
</div> </div>
</el-card>
<el-card shadow="hover" style="height: 252px">
<template #header>
<div class="clearfix">
<span>语言详情</span>
</div>
</template>
Vue
<el-progress :percentage="79.4" color="#42b983"></el-progress>
TypeScript
<el-progress :percentage="14" color="#f1e05a"></el-progress>
CSS
<el-progress :percentage="5.6"></el-progress>
HTML
<el-progress :percentage="1" color="#f56c6c"></el-progress>
</el-card>
</el-col>
<el-col :span="16">
<el-row :gutter="20" class="mgb20">
<el-col :span="8">
<el-card shadow="hover" :body-style="{ padding: '0px' }">
<div class="grid-content grid-con-1">
<el-icon class="grid-con-icon"><User /></el-icon>
<div class="grid-cont-right">
<div class="grid-num">1234</div>
<div>用户访问量</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" :body-style="{ padding: '0px' }">
<div class="grid-content grid-con-2">
<el-icon class="grid-con-icon"><ChatDotRound /></el-icon>
<div class="grid-cont-right">
<div class="grid-num">321</div>
<div>系统消息</div>
</div>
</div>
</el-card>
</el-col>
<el-col :span="8">
<el-card shadow="hover" :body-style="{ padding: '0px' }">
<div class="grid-content grid-con-3">
<el-icon class="grid-con-icon"><Goods /></el-icon>
<div class="grid-cont-right">
<div class="grid-num">500</div>
<div>商品数量</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
<el-card shadow="hover" style="height: 403px">
<template #header>
<div class="clearfix">
<span>预警列表</span>
<el-button style="float: right; padding: 3px 0" text>添加</el-button>
</div>
</template>
<el-table :show-header="false" :data="todoList" style="width: 100%">
<el-table-column width="40">
<template #default="scope">
<el-checkbox v-model="scope.row.status"></el-checkbox>
</template>
</el-table-column>
<el-table-column>
<template #default="scope">
<div
class="todo-item"
:class="{
'todo-item-del': scope.row.status
}"
>
{{ scope.row.title }}
</div>
</template>
</el-table-column>
</el-table>
</el-card>
</el-col>
</el-row>
</div>
</template> </template>
<script setup lang="ts" name="dashboard"> <script setup lang="ts" name="dashboard">
import { ref, reactive } from 'vue'; import { ref, reactive } from 'vue';
import imgurl from '../assets/img/img.jpg'; import imgurl from '../assets/img/img.jpg';
const roleMap = {
}
const name = localStorage.getItem('ms_username'); const name = localStorage.getItem('ms_username');
const role: string = name === 'admin' ? '超级管理员' : '普通用户'; const role: string = name === 'admin' ? '超级管理员' : '普通用户';
</script> </script>
<style scoped> <style scoped></style>
.el-row {
margin-bottom: 20px;
}
.grid-content {
display: flex;
align-items: center;
height: 100px;
}
.grid-cont-right {
flex: 1;
text-align: center;
font-size: 14px;
color: #999;
}
.grid-num {
font-size: 30px;
font-weight: bold;
}
.grid-con-icon {
font-size: 50px;
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
color: #fff;
}
.grid-con-1 .grid-con-icon {
background: rgb(45, 140, 240);
}
.grid-con-1 .grid-num {
color: rgb(45, 140, 240);
}
.grid-con-2 .grid-con-icon {
background: rgb(100, 213, 114);
}
.grid-con-2 .grid-num {
color: rgb(100, 213, 114);
}
.grid-con-3 .grid-con-icon {
background: rgb(242, 94, 67);
}
.grid-con-3 .grid-num {
color: rgb(242, 94, 67);
}
.user-info {
display: flex;
align-items: center;
padding-bottom: 20px;
border-bottom: 2px solid #ccc;
margin-bottom: 20px;
}
.user-info-cont {
padding-left: 50px;
flex: 1;
font-size: 14px;
color: #999;
}
.user-info-cont div:first-child {
font-size: 30px;
color: #222;
}
.user-info-list {
font-size: 14px;
color: #999;
line-height: 25px;
}
.user-info-list span {
margin-left: 70px;
}
.mgb20 {
margin-bottom: 20px;
}
.todo-item {
font-size: 14px;
}
.todo-item-del {
text-decoration: line-through;
color: #999;
}
</style>

View File

@ -116,7 +116,10 @@ const submitForm = (formEl: FormInstance | undefined) => {
if (!data) return; if (!data) return;
console.log("login data", data, data.userInfo); console.log("login data", data, data.userInfo);
if (![1, 2].includes(data.userInfo?.roleId)) {
ElMessage.error('您所在用户组无权登录当前系统');
return;
}
ElMessage.success('登录成功'); ElMessage.success('登录成功');
localStorage.setItem('ms_username', data.userInfo?.username); localStorage.setItem('ms_username', data.userInfo?.username);
localStorage.setItem('ms_realname', data.userInfo?.realname); localStorage.setItem('ms_realname', data.userInfo?.realname);

View File

@ -0,0 +1,191 @@
<template>
<div class="container">
<manageList :list-func="shopOrderApi.getOrderList" :custom-edit-handle="editHandle"
:export-func="shopOrderApi.exportOrderList" edit-permiss="shop-order-setting" />
<!-- 新增 / 编辑弹出框 -->
<el-dialog title="管理订单" v-model="visible" style="width: 60%; min-width: 280px;">
<el-row>
<el-col :span="12">
<p class="line-height">
<span class="row-index">订单ID</span>
{{ orderDetail.id || "" }}
</p>
<p class="line-height">
<span class="row-index">订单状态</span>
<el-tag effect="plain" round>{{ orderDetail.orderStatus || "" }}</el-tag>
{{ orderDetail.orderStatusCode || "" }}
</p>
<p class="line-height">
<span class="row-index">下单用户ID</span>
{{ orderDetail.userId || "" }}
</p>
<p class="line-height">
<span class="row-index">订单价格</span>
{{ orderDetail.orderPrice ? ('¥' + orderDetail.orderPrice) : "-" }}
</p>
<p class="line-height">
<span class="row-index">下单时间</span>
{{ orderDetail.orderDate || "-" }}
</p>
<p class="line-height">
<span class="row-index">订单取消时间</span>
{{ orderDetail.cancelDate || "-" }}
</p>
<p class="line-height">
<span class="row-index">订单支付时间</span>
{{ orderDetail.payDate || "-" }}
</p>
<p class="line-height">
<span class="row-index">订单发货时间</span>
{{ orderDetail.shipDate || "-" }}
</p>
<p class="line-height">
<span class="row-index">订单送达时间</span>
{{ orderDetail.deliverDate || "-" }}
</p>
<p class="line-height">
<span class="row-index">运单号</span>
<template
v-if="orderDetail.orderStatusCode != 'Processing' && orderDetail.orderStatusCode != 'Shipped'">
{{ orderDetail.expressId || "-" }}
</template>
<el-input v-else v-model="shippingInfo.expressId" placeholder="Please input" />
</p>
<p class="line-height">
<span class="row-index">发货备注</span>
<template
v-if="orderDetail.orderStatusCode != 'Processing' && orderDetail.orderStatusCode != 'Shipped'">
{{ orderDetail.comment || "-" }}
</template>
<el-input v-else v-model="shippingInfo.comment" placeholder="Please input" />
</p>
</el-col>
<el-col :span="12">
<p style="margin-bottom: 20px;">该订单中包含如下商品</p>
<div style="max-height:50vh; overflow-y: scroll;">
<el-card class="box-card" v-for="i in orderItem" :key="i.goodId"
style="--el-card-padding: 10px; margin-bottom: 8px;">
<div style="display: grid; grid-template-columns: 50px 1fr 60px; gap: 15px;">
<img :src="i.good.picUrl" style="width: 60px; height: 60px;" />
<div style="place-self: center left;">
<p>{{ i.good.goodsName }}</p>
<p style="color: grey; font-size: 12px;">商品ID: {{ i.goodId }}</p>
</div>
<div style="place-self: center; font-size: 16px;">{{ i.goodCount }} {{ i.good.unit }}</div>
</div>
</el-card>
</div>
</el-col>
</el-row>
<!-- 订单状态PENDING("Pending", "等待确认"), -->
<!-- 订单状态PROCESSING("Processing", "已支付,等待发货中"), -->
<!-- 订单状态SHIPPED("Shipped", "已发货,等待确认收货"), -->
<!-- 订单状态DELIVERED("Delivered", "已送达"), -->
<!-- 订单状态CANCELLED("Cancelled", "已取消"); -->
<!-- 该订单已被取消 -->
<template #footer>
<span class="dialog-footer">
<el-button v-if="orderDetail.orderStatusCode == 'Processing'" type="danger"
@click="withdrawOrder">取消发货并退款</el-button>
<el-button
v-if="orderDetail.orderStatusCode == 'Processing' || orderDetail.orderStatusCode == 'Shipped'"
type="primary" @click="saveEdit">保存发货信息</el-button>
<el-button @click="visible = false"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import manageList from '../components/manage-list.vue';
import * as shopOrderApi from '../api/shop-order';
import { ref, reactive, onMounted, computed } from 'vue';
import { FormInstance, FormRules, ElMessage, ElMessageBox } from 'element-plus';
import { Delete, Edit, Search, Plus, Filter, Download } from '@element-plus/icons-vue';
const visible: any = ref(false);
const orderDetail = ref({} as any);
const orderItem = ref([] as any);
const shippingInfo = ref({} as any); //
let completeCallback: Function = () => { };
const editHandle = async function (tabIndex: number, row: any, refreshFunc: Function) {
console.log("tabIndex", tabIndex)
console.log("orderDetail", row)
orderDetail.value = row //
shippingInfo.value.expressId = row.expressId
shippingInfo.value.comment = row.comment
completeCallback = refreshFunc
visible.value = true
shopOrderApi.getOrderDetail({
orderId: row.id
}).then(function (data) {
let orderGoods = {}
data.goods.forEach((good: any) => {
orderGoods[good.id] = good
});
orderItem.value = data.orderItem.map((item: any) => {
item.good = orderGoods[item.goodId]
return item
})
console.log("orderItem", orderItem.value)
})
}
const saveEdit = function () {
//
ElMessageBox.confirm('确定要进行发货操作吗?发货后订单将不可撤销。(用户点击收货前仍可以修改发货信息)', '提示', { type: 'warning' })
.then(async () => {
shopOrderApi.deliverOrder({
orderId: orderDetail.value.id,
...shippingInfo.value
}).then(function (data) {
if (data) { //
ElMessage.success({ message: data })
visible.value = false
completeCallback()
}
})
})
.catch(() => { });
}
const withdrawOrder = function () {
//
ElMessageBox.confirm('确定要取消此订单吗?该操作不可撤销。', '提示', { type: 'warning' })
.then(async () => {
shopOrderApi.withdrawOrder({
orderId: orderDetail.value.id,
...shippingInfo.value
}).then(function (data) {
if (data) { //
ElMessage.success({ message: data })
visible.value = false
completeCallback()
}
})
})
.catch(() => { });
}
</script>
<style>
.line-height {
line-height: 2.7em;
}
.row-index {
width: 110px;
display: inline-block;
}
</style>

View File

@ -1,67 +1,63 @@
<template> <template>
<div> <div>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="12"> <el-col :span="12">
<el-card shadow="hover"> <el-card shadow="hover">
<template #header> <template #header>
<div class="clearfix"> <div class="clearfix">
<span>基础信息</span> <span>基础信息</span>
</div> </div>
</template> </template>
<div class="info"> <div class="info">
<div class="info-image" @click="showDialog"> <div class="info-image" @click="showDialog">
<el-avatar :size="100" :src="avatarImg" /> <el-avatar :size="100" :src="avatarImg" />
<span class="info-edit"> <span class="info-edit">
<i class="el-icon-lx-camerafill"></i> <i class="el-icon-lx-camerafill"></i>
</span> </span>
</div> </div>
<div class="info-name">{{ name }}</div> <div class="info-name">{{ name }}</div>
<!-- <div class="info-desc">不可能我的代码怎么可能会有bug</div> --> </div>
</div> </el-card>
</el-card> </el-col>
</el-col> <el-col :span="12">
<el-col :span="12"> <el-card shadow="hover">
<el-card shadow="hover"> <template #header>
<template #header> <div class="clearfix">
<div class="clearfix"> <span>账户编辑</span>
<span>账户编辑</span> </div>
</div> </template>
</template> <el-form label-width="90px">
<el-form label-width="90px"> <el-form-item label="用户名:"> {{ name }} </el-form-item>
<el-form-item label="用户名:"> {{ name }} </el-form-item> <el-form-item label="旧密码:">
<el-form-item label="旧密码:"> <el-input type="password" v-model="form.old"></el-input>
<el-input type="password" v-model="form.old"></el-input> </el-form-item>
</el-form-item> <el-form-item label="新密码:">
<el-form-item label="新密码:"> <el-input type="password" v-model="form.new"></el-input>
<el-input type="password" v-model="form.new"></el-input> </el-form-item>
</el-form-item> <el-form-item label="确认密码:">
<el-form-item label="确认密码:"> <el-input type="password" v-model="form.new1"></el-input>
<el-input type="password" v-model="form.new1"></el-input> </el-form-item>
</el-form-item> <el-form-item>
<!-- <el-form-item label="个人简介:"> <el-button type="primary" @click="onSubmit">保存</el-button>
<el-input v-model="form.desc"></el-input> </el-form-item>
</el-form-item> --> </el-form>
<el-form-item> </el-card>
<el-button type="primary" @click="onSubmit">保存</el-button> </el-col>
</el-form-item> </el-row>
</el-form> <el-dialog title="裁剪图片" v-model="dialogVisible" width="600px">
</el-card> <vue-cropper ref="cropper" :src="imgSrc" :ready="cropImage" :zoom="cropImage" :cropmove="cropImage"
</el-col> style="width: 100%; height: 400px"></vue-cropper>
</el-row>
<el-dialog title="裁剪图片" v-model="dialogVisible" width="600px">
<vue-cropper ref="cropper" :src="imgSrc" :ready="cropImage" :zoom="cropImage" :cropmove="cropImage"
style="width: 100%; height: 400px"></vue-cropper>
<template #footer> <template #footer>
<span class="dialog-footer"> <span class="dialog-footer">
<el-button class="crop-demo-btn" type="primary">选择图片 <el-button class="crop-demo-btn" type="primary">选择图片
<input class="crop-input" type="file" name="image" accept="image/*" @change="setImage" /> <input class="crop-input" type="file" name="image" accept="image/*" @change="setImage" />
</el-button> </el-button>
<el-button type="primary" @click="saveAvatar">上传并保存</el-button> <el-button type="primary" @click="saveAvatar">上传并保存</el-button>
</span> </span>
</template> </template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup lang="ts" name="user"> <script setup lang="ts" name="user">
@ -70,65 +66,52 @@ import VueCropper from 'vue-cropperjs';
import 'cropperjs/dist/cropper.css'; import 'cropperjs/dist/cropper.css';
import avatar from '../assets/img/img.jpg'; import avatar from '../assets/img/img.jpg';
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus'; import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import send_request from '../utils/send_request'; import * as userApi from '../api/user';
const name = localStorage.getItem('ms_username'); const name = localStorage.getItem('ms_username');
const user_id = localStorage.getItem('ms_user_id'); const user_id = localStorage.getItem('ms_user_id');
const form = reactive({ const form = reactive({
user_id: user_id, user_id: user_id,
old: '', old: '',
new: '', new: '',
new1: '', new1: '',
desc: '' desc: ''
}); });
const onSubmit = async () => { const onSubmit = async () => {
if (form.old == '' || form.new == '' || form.new1 == '') { if (form.old == '' || form.new == '' || form.new1 == '') {
// //
ElMessageBox.confirm('输入为空,请检查', '提示', { ElMessageBox.confirm('输入为空,请检查', '提示', {
type: 'warning' type: 'warning'
}) })
return return
} else if (form.new != form.new1) { } else if (form.new != form.new1) {
// //
ElMessageBox.confirm('新密码2次输入的不相同', '提示', { ElMessageBox.confirm('新密码2次输入的不相同', '提示', {
type: 'warning' type: 'warning'
}) })
return return
} else if (form.new == form.old) { } else if (form.new == form.old) {
// //
ElMessageBox.confirm('新、旧密码相同', '提示', { ElMessageBox.confirm('新、旧密码相同', '提示', {
type: 'warning' type: 'warning'
}) })
return return
} }
ElMessageBox.confirm('确认要修改密码吗?', '提示', { ElMessageBox.confirm('确认要修改密码吗?', '提示', {
type: 'warning' type: 'warning'
}) }).then(async () => {
.then(async () => { userApi.updatePwd({
console.log("send_request v1/user/alterPSW") oldpwd: form.old,
newpwd: form.new,
const loading = ElLoading.service({ }).then((data) => {
lock: true, data && ElMessage.success(data);
text: '请稍候', })
background: 'rgba(0, 0, 0, 0.7)', }).catch((err) => {
}); ElMessage.success('删除失败');
await send_request('v1/user/alterPSW', "POST", { console.log("err", err)
"userId": form.user_id, });
"oldPSW": form.old, return;
"newPSW": form.new
}, (data: any) => {
console.log(data);
ElMessage.success('删除成功');
// tableData.value.splice(index, 1);
})
loading.close();
})
.catch(() => {
ElMessage.success('删除失败');
});
return;
}; };
@ -139,92 +122,92 @@ const dialogVisible = ref(false);
const cropper: any = ref(); const cropper: any = ref();
const showDialog = () => { const showDialog = () => {
dialogVisible.value = true; dialogVisible.value = true;
imgSrc.value = avatarImg.value; imgSrc.value = avatarImg.value;
}; };
const setImage = (e: any) => { const setImage = (e: any) => {
const file = e.target.files[0]; const file = e.target.files[0];
if (!file.type.includes('image/')) { if (!file.type.includes('image/')) {
return; return;
} }
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (event: any) => { reader.onload = (event: any) => {
dialogVisible.value = true; dialogVisible.value = true;
imgSrc.value = event.target.result; imgSrc.value = event.target.result;
cropper.value && cropper.value.replace(event.target.result); cropper.value && cropper.value.replace(event.target.result);
}; };
reader.readAsDataURL(file); reader.readAsDataURL(file);
}; };
const cropImage = () => { const cropImage = () => {
cropImg.value = cropper.value.getCroppedCanvas().toDataURL(); cropImg.value = cropper.value.getCroppedCanvas().toDataURL();
}; };
const saveAvatar = () => { const saveAvatar = () => {
avatarImg.value = cropImg.value; avatarImg.value = cropImg.value;
dialogVisible.value = false; dialogVisible.value = false;
}; };
</script> </script>
<style scoped> <style scoped>
.info { .info {
text-align: center; text-align: center;
padding: 35px 0; padding: 35px 0;
} }
.info-image { .info-image {
position: relative; position: relative;
margin: auto; margin: auto;
width: 100px; width: 100px;
height: 100px; height: 100px;
background: #f8f8f8; background: #f8f8f8;
border: 1px solid #eee; border: 1px solid #eee;
border-radius: 50px; border-radius: 50px;
overflow: hidden; overflow: hidden;
} }
.info-edit { .info-edit {
display: flex; display: flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
opacity: 0; opacity: 0;
transition: opacity 0.3s ease; transition: opacity 0.3s ease;
} }
.info-edit i { .info-edit i {
color: #eee; color: #eee;
font-size: 25px; font-size: 25px;
} }
.info-image:hover .info-edit { .info-image:hover .info-edit {
opacity: 1; opacity: 1;
} }
.info-name { .info-name {
margin: 15px 0 10px; margin: 15px 0 10px;
font-size: 24px; font-size: 24px;
font-weight: 500; font-weight: 500;
color: #262626; color: #262626;
} }
.crop-demo-btn { .crop-demo-btn {
position: relative; position: relative;
} }
.crop-input { .crop-input {
position: absolute; position: absolute;
width: 100px; width: 100px;
height: 40px; height: 40px;
left: 0; left: 0;
top: 0; top: 0;
opacity: 0; opacity: 0;
cursor: pointer; cursor: pointer;
} }
</style> </style>

View File

@ -4,6 +4,13 @@ server
listen 80; listen 80;
listen 443 ssl http2; listen 443 ssl http2;
# 并发限制 限制当前站点最大并发数
limit_conn perserver 50;
# 单IP限制 限制单个IP访问最大并发数
limit_conn perip 10;
# 流量限制 限制每个请求的流量上限单位KB
limit_rate 8192k;
location / { location / {
proxy_pass http://127.0.0.1:5203; proxy_pass http://127.0.0.1:5203;

View File

@ -22,9 +22,14 @@ App({
}, },
globalData: { globalData: {
// debugMode: true, // 是否展示调试内容 // debugMode: true, // 是否展示调试内容
baseUrl: true ? // Api 请求域名 不带最后的 / /**
"https://epp-prod.only4.work" : * FIXME 环境配置
"http://localhost", *
* baseUrl
* - 线上环境'https://epp.only4.work'
* - 开发环境'http://localhost'
*/
baseUrl: "https://epp-prod.only4.work", // Api 请求域名 不带最后的 /
userInfo: null userInfo: null
} }
}) })

View File

@ -137,7 +137,7 @@ Page({
filterList: filterList, filterList: filterList,
filterActiveName: "全部", filterActiveName: "全部",
orderList: userOrder.orders.map(order => { orderList: userOrder.orders.map(order => {
order.displayDate = order.orderDate.replace("T", " ") order.displayDate = order.orderDate
return order return order
}) })
}) })
@ -153,7 +153,7 @@ Page({
console.log("userOrder", userOrder) console.log("userOrder", userOrder)
this.setData({ this.setData({
orderList: userOrder.orders.map(order => { orderList: userOrder.orders.map(order => {
order.displayDate = order.orderDate.replace("T", " ") order.displayDate = order.orderDate
return order return order
}) })
}) })

View File

@ -259,7 +259,7 @@ Page({
this.setData({ this.setData({
order: orderDetail.order, order: orderDetail.order,
orderGoodList: orderGoodList, orderGoodList: orderGoodList,
orderTime: orderDetail.order.orderDate.replace("T", " "), orderTime: orderDetail.order.orderDate,
orderStatusCode: orderStatusCode, orderStatusCode: orderStatusCode,
orderPrice: orderPrice, orderPrice: orderPrice,
}) })

View File

@ -3,7 +3,7 @@
"projectname": "weixin-miniprogram", "projectname": "weixin-miniprogram",
"setting": { "setting": {
"compileHotReLoad": true, "compileHotReLoad": true,
"urlCheck": true, "urlCheck": false,
"bigPackageSizeSupport": true "bigPackageSizeSupport": true
} }
} }

View File

@ -1,26 +1,21 @@
> 使用 phpMyAdmin 导出,如果有修改,可以同步修改到论文正文 > 使用 phpMyAdmin 导出,如果有修改,可以同步修改到论文正文
## apply1 ## access_log
| 字段 | 类型 | 空 | 默认 | 注释 | 表注释: *人员进出记录表*
| :---------- | :---------- | :--- | :----- | :--------------------------- |
| id *(主键)* | int | 否 | | |
| stu_id | int | 是 | *NULL* | 用户id |
| issue | varchar(50) | 是 | *NULL* | 申请事由 |
| transport | varchar(20) | 是 | *NULL* | 出行方式 |
| place | varchar(50) | 是 | *NULL* | 目的地 |
| start_time | datetime | 是 | *NULL* | 开始时间 |
| end_time | datetime | 是 | *NULL* | 结束时间 |
| state | int | 是 | 0 | 状态(0:审批中,1:通过,2:驳回) |
| reason | varchar(20) | 是 | *NULL* | 驳回原因 |
### 索引 | 字段 | 类型 | 空 | 默认 | 注释 |
| :------------- | :---------------- | :--- | :---------------- | :------------------- |
| id | bigint | 否 | | 雪花id |
| time | datetime | 否 | CURRENT_TIMESTAMP | 进出时间 |
| user_id | int | 否 | | 用户id |
| user_real_name | varchar(255) | 是 | *NULL* | 用户真实姓名 |
| gate_id | bigint | 否 | | 大门id |
| type | enum('IN', 'OUT') | 是 | *NULL* | 类型(进门 OR 出门) |
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 | 没有已定义的索引!
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 10 | A | 否 | |
## community_gate ## gate
表注释: *社区大门* 表注释: *社区大门*
@ -32,38 +27,6 @@
### 索引 ### 索引
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 0 | A | 否 | |
## count1
| 字段 | 类型 | 空 | 默认 | 注释 |
| :------------ | :------- | :--- | :----- | :------- |
| date *(主键)* | date | 否 | | 日期 |
| time | datetime | 是 | *NULL* | 时间 |
| in_num | int | 是 | *NULL* | 入校人数 |
| out_num | int | 是 | *NULL* | 出校人数 |
### 索引
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | date | 0 | A | 否 | |
## feedback1
| 字段 | 类型 | 空 | 默认 | 注释 |
| :---------- | :----------- | :--- | :----- | :--------- |
| id *(主键)* | int | 否 | | |
| title | varchar(255) | 是 | *NULL* | 反馈标题 |
| content | longtext | 是 | *NULL* | 反馈内容 |
| time | datetime | 是 | *NULL* | 反馈时间 |
| user_id | int | 是 | *NULL* | 反馈用户id |
| reply | varchar(255) | 是 | *NULL* | 回复 |
### 索引
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 | | 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- | | :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 5 | A | 否 | | | PRIMARY | BTREE | 是 | 否 | id | 5 | A | 否 | |
@ -94,10 +57,10 @@
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 | | 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :---------- | :---- | :--- | :--- | :---------- | :--- | :------- | :--- | :--- | | :---------- | :---- | :--- | :--- | :---------- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 87 | A | 否 | | | PRIMARY | BTREE | 是 | 否 | id | 89 | A | 否 | |
| category_id | BTREE | 否 | 否 | category_id | 8 | A | 是 | | | category_id | BTREE | 否 | 否 | category_id | 8 | A | 是 | |
| brand_id | BTREE | 否 | 否 | brand | 67 | A | 是 | | | brand_id | BTREE | 否 | 否 | brand | 69 | A | 是 | |
| sort_order | BTREE | 否 | 否 | sort_order | 25 | A | 是 | | | sort_order | BTREE | 否 | 否 | sort_order | 27 | A | 是 | |
## goods_category ## goods_category
@ -111,22 +74,7 @@
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 | | 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- | | :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 7 | A | 否 | | | PRIMARY | BTREE | 是 | 否 | id | 8 | A | 否 | |
## notice1
| 字段 | 类型 | 空 | 默认 | 注释 |
| :---------- | :----------- | :--- | :----- | :--- |
| id *(主键)* | int | 否 | | |
| title | varchar(255) | 是 | *NULL* | 标题 |
| content | longtext | 是 | *NULL* | 内容 |
| time | date | 是 | *NULL* | 时间 |
### 索引
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 5 | A | 否 | |
## order ## order
@ -138,13 +86,18 @@
| order_status | enum('Pending', 'Processing', 'Shipped', 'Delivered', 'Cancelled') | 否 | | 订单状态 | | order_status | enum('Pending', 'Processing', 'Shipped', 'Delivered', 'Cancelled') | 否 | | 订单状态 |
| order_price | decimal(10,2) | 否 | | 订单总金额 | | order_price | decimal(10,2) | 否 | | 订单总金额 |
| pay_date | datetime | 是 | *NULL* | 订单支付时间 | | pay_date | datetime | 是 | *NULL* | 订单支付时间 |
| cancel_date | datetime | 是 | *NULL* | 订单取消时间 |
| ship_date | datetime | 是 | *NULL* | 订单发货时间 |
| deliver_date | datetime | 是 | *NULL* | 订单送达时间 |
| express_id | varchar(500) | 是 | *NULL* | 快递单号 |
| comment | text | 是 | *NULL* | 发货备注 |
### 索引 ### 索引
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 | | 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :------ | :--- | :------- | :--- | :--- | | :------ | :---- | :--- | :--- | :------ | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 4 | A | 否 | | | PRIMARY | BTREE | 是 | 否 | id | 15 | A | 否 | |
| id | BTREE | 是 | 否 | id | 4 | A | 否 | | | id | BTREE | 是 | 否 | id | 15 | A | 否 | |
| user_id | BTREE | 否 | 否 | user_id | 1 | A | 否 | | | user_id | BTREE | 否 | 否 | user_id | 1 | A | 否 | |
## order_detail ## order_detail
@ -161,9 +114,9 @@
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 | | 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------- | :---- | :--- | :--- | :------- | :--- | :------- | :--- | :--- | | :------- | :---- | :--- | :--- | :------- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 10 | A | 否 | | | PRIMARY | BTREE | 是 | 否 | id | 21 | A | 否 | |
| order_id | BTREE | 否 | 否 | order_id | 4 | A | 否 | | | order_id | BTREE | 否 | 否 | order_id | 15 | A | 否 | |
| good_id | BTREE | 否 | 否 | good_id | 2 | A | 否 | | | good_id | BTREE | 否 | 否 | good_id | 9 | A | 否 | |
## report ## report
@ -180,7 +133,20 @@
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 | | 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- | | :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 1 | A | 否 | | | PRIMARY | BTREE | 是 | 否 | id | 12 | A | 否 | |
## role
| 字段 | 类型 | 空 | 默认 | 注释 |
| :---------- | :----------- | :--- | :--- | :------- |
| id *(主键)* | int | 否 | | role_id |
| role_name | varchar(255) | 否 | | roleName |
### 索引
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 6 | A | 否 | |
## setting ## setting
@ -198,42 +164,27 @@
## user ## user
| 字段 | 类型 | 空 | 默认 | 注释 | | 字段 | 类型 | 空 | 默认 | 注释 |
| :-------------- | :----------- | :--- | :----- | :----------------------------------------------------------- | | :-------------- | :----------- | :--- | :----- | :--------------------------------------------- |
| id *(主键)* | int | 否 | | | | id *(主键)* | int | 否 | | |
| username | varchar(20) | 否 | | 用户名 | | username | varchar(20) | 否 | | 用户名 |
| password | varchar(255) | 否 | | 密码 | | password | varchar(255) | 否 | | 密码 |
| realname | varchar(20) | 是 | *NULL* | 真实姓名 | | realname | varchar(20) | 是 | *NULL* | 真实姓名 |
| id_number | varchar(18) | 是 | *NULL* | 身份证号 | | id_number | varchar(18) | 是 | *NULL* | 身份证号 |
| phone_number | varchar(11) | 是 | *NULL* | 手机号 | | phone_number | varchar(20) | 是 | *NULL* | 手机号 |
| role | int | 否 | | 角色 (0-超级管理员 1-工作人员 2-社区居民_房主 3-社区居民_家庭成员 4-社区居民_租客 5-访客) | | role_id | int | 否 | | 角色id |
| building_id | varchar(255) | 是 | *NULL* | 门栋号+单元号 | | building_id | varchar(255) | 是 | *NULL* | 门栋号+单元号 |
| doorplate | varchar(255) | 是 | *NULL* | 门牌号 | | doorplate | varchar(255) | 是 | *NULL* | 门牌号 |
| permission | int | 否 | 0 | 进出权限 (0-无 1-继承(普通居民) 2-永久 3-限时) | | permission | varchar(20) | 否 | 0 | 进出权限 (0-无 1-继承(普通居民) 2-永久 3-限时) |
| permission_time | datetime | 是 | *NULL* | 进出权限失效时间 | | permission_time | datetime | 是 | *NULL* | 进出权限失效时间 |
| wx_code | varchar(255) | 是 | *NULL* | 微信登录授权码 |
### 索引
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 1 | A | 否 | |
## visitor1
| 字段 | 类型 | 空 | 默认 | 注释 |
| :---------- | :----------- | :--- | :----- | :---------------------------------- |
| id *(主键)* | int | 否 | | |
| time | datetime | 是 | *NULL* | 预约时间 |
| phone | varchar(20) | 是 | *NULL* | 预约用户id |
| issue | varchar(255) | 是 | *NULL* | 预约事由 |
| meet_name | varchar(20) | 是 | *NULL* | 会见人姓名 |
| create_time | datetime | 是 | *NULL* | 创建时间 |
| state | int | 是 | 0 | 状态0审批中1成功2驳回 |
### 索引 ### 索引
| 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 | | 键名 | 类型 | 唯一 | 紧凑 | 字段 | 基数 | 排序规则 | 空 | 注释 |
| :------ | :---- | :--- | :--- | :--- | :--- | :------- | :--- | :--- | | :------- | :---- | :--- | :--- | :------- | :--- | :------- | :--- | :--- |
| PRIMARY | BTREE | 是 | 否 | id | 1 | A | 否 | | | PRIMARY | BTREE | 是 | 否 | id | 1 | A | 否 | |
| username | BTREE | 是 | 否 | username | 1 | A | 否 | |
| wx_code | BTREE | 是 | 否 | wx_code | 1 | A | 是 | |