Merge branch 'master' of gitee.com:lab1024/smart-admin into master

Signed-off-by: 大熊 <daxiongren@foxmail.com>
This commit is contained in:
大熊 2024-03-20 07:51:46 +00:00 committed by Gitee
commit d4ae31cad0
311 changed files with 22477 additions and 71 deletions

View File

@ -2,16 +2,17 @@
**SmartAdmin** 由 **河南·洛阳** [1024创新实验室](https://www.1024lab.net/) 坚持以 <font color="#DC143C">**「高质量代码」为核心,「简洁、高效、安全」**</font>的中后台解决方案! **SmartAdmin** 由 **河南·洛阳** [1024创新实验室](https://www.1024lab.net/) 坚持以 <font color="#DC143C">**「高质量代码」为核心,「简洁、高效、安全」**</font>的中后台解决方案!
<font color="#DC143C">**国内首个满足《网络安全》、《数据安全》、三级等保**</font> 支持登录限制、支持接口国产加解密、支持数据加解密等一系列安全措施的开源项目。 <font color="#DC143C">**国内满足《网络安全》、《数据安全》、三级等保**</font> 支持登录限制、支持接口国产加解密、支持数据加解密等一系列安全措施的开源项目。
<font color="#DC143C">**我们开源一套漂亮的代码和一套整洁的代码规范**</font>,让大家在这浮躁的代码世界里感受到一股把代码写好的清流!同时又让开发者节省大量的时间,减少加班,快乐工作,保持谦逊,保持学习,**热爱代码,更热爱生活** <font color="#DC143C">**我们开源一套漂亮的代码和一套整洁的代码规范**</font>,让大家在这浮躁的代码世界里感受到一股把代码写好的清流!同时又让开发者节省大量的时间,减少加班,快乐工作,保持谦逊,保持学习,**热爱代码,更热爱生活**
### **技术体系** ### **技术体系**
- 前端Vue3 + Vite5 + Vue-Router + Pinia + Ant Design Vue 4.X - 前端Vue3 + Vite5 + Vue-Router + Pinia + Ant Design Vue 4.X
- 移动端uniapp + uview2.x - 移动端uniapp (vue3版本) + uni-ui + 同时支持APP、小程序、H5
- 后端SpringBoot + Sa Token + Mybatis-plus + 多种数据库 - 后端SpringBoot + Sa Token + Mybatis-plus + 多种数据库
- 在线预览:[https://preview.smartadmin.vip](https://preview.smartadmin.vip) - 电脑在线预览:[https://preview.smartadmin.vip](https://preview.smartadmin.vip)
- 官方文档:[https://smartadmin.vip](https://smartadmin.vip) - 官方文档:[https://smartadmin.vip](https://smartadmin.vip)
- 移动端在线预览:[https://app.smartadmin.vip](https://app.smartadmin.vip/#/pages/login/login)
### **理念与思想** ### **理念与思想**
- 我们分享的不是徒劳无功的各种功能,而是必须有的功能,如:网络安全、数据变动记录、系统说明文档、版本更新记录、意见反馈、日志、心跳、单号生成器等等。 - 我们分享的不是徒劳无功的各种功能,而是必须有的功能,如:网络安全、数据变动记录、系统说明文档、版本更新记录、意见反馈、日志、心跳、单号生成器等等。
@ -74,22 +75,5 @@
### **联系我们**
[1024 创新实验室-主任:卓大](https://zhuoda.vip),混迹于各个技术圈,研究过计算机,熟悉点 java略懂点前端。
[1024 创新实验室(河南·洛阳)](https://1024lab.net) 致力于成为中原领先、国内一流的技术团队,以技术创新为驱动,合作各类项目(软件外包、技术顾问、培训等等)。
<table>
<tr>
<td><img src="https://img.smartadmin.1024lab.net/wechat/zhuoda-wechat.jpg" width="250" /></td>
<td><img src="https://img.smartadmin.1024lab.net/wechat/liubianxing-gzh.jpg" width="250" /></td>
<td><img src="https://img.smartadmin.1024lab.net/wechat/zhuoda-wechat-money-v1.jpg" width="250" /></td>
</tr>
<tr>
<td style="text-align:center">加 主任 “卓大” 微信 <br> 拉你入群,一起学习</td>
<td style="text-align:center">关注 “六边形工程师” <br> 分享:赚钱、代码、生活</td>
<td style="text-align:center">请 “1024创新实验室” 喝咖啡 <br> 支持我们的开源与分享 </td>
</tr>
</table>

View File

@ -109,6 +109,11 @@ public class AdminInterceptor implements HandlerInterceptor {
return true; return true;
} }
// 如果是超级管理员的话不需要校验权限
if(requestEmployee.getAdministratorFlag()){
return true;
}
SaStrategy.instance.checkMethodAnnotation.accept(method); SaStrategy.instance.checkMethodAnnotation.accept(method);
} catch (SaTokenException e) { } catch (SaTokenException e) {

View File

@ -44,11 +44,9 @@ public class NoticeVO {
private LocalDateTime publishTime; private LocalDateTime publishTime;
@Schema(description = "作者") @Schema(description = "作者")
@NotBlank(message = "作者不能为空")
private String author; private String author;
@Schema(description = "来源") @Schema(description = "来源")
@NotBlank(message = "标题不能为空")
private String source; private String source;
@Schema(description = "文号") @Schema(description = "文号")

View File

@ -1,5 +1,6 @@
package net.lab1024.sa.admin.module.system.login.controller; package net.lab1024.sa.admin.module.system.login.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.extra.servlet.ServletUtil; import cn.hutool.extra.servlet.ServletUtil;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@ -47,7 +48,10 @@ public class LoginController {
@GetMapping("/login/getLoginInfo") @GetMapping("/login/getLoginInfo")
@Operation(summary = "获取登录结果信息 @author 卓大") @Operation(summary = "获取登录结果信息 @author 卓大")
public ResponseDTO<LoginResultVO> getLoginInfo() { public ResponseDTO<LoginResultVO> getLoginInfo() {
return ResponseDTO.ok(loginService.getLoginResult(AdminRequestUtil.getRequestUser())); LoginResultVO loginResult = loginService.getLoginResult(AdminRequestUtil.getRequestUser());
String tokenValue = StpUtil.getTokenValue();
loginResult.setToken(tokenValue);
return ResponseDTO.ok(loginResult);
} }
@Operation(summary = "退出登陆 @author 卓大") @Operation(summary = "退出登陆 @author 卓大")

View File

@ -25,12 +25,12 @@ public class PageParam {
@Schema(description = "页码(不能为空)", example = "1") @Schema(description = "页码(不能为空)", example = "1")
@NotNull(message = "分页参数不能为空") @NotNull(message = "分页参数不能为空")
private Integer pageNum; private Long pageNum;
@Schema(description = "每页数量(不能为空)", example = "10") @Schema(description = "每页数量(不能为空)", example = "10")
@NotNull(message = "每页数量不能为空") @NotNull(message = "每页数量不能为空")
@Max(value = 200, message = "每页最大为200") @Max(value = 500, message = "每页最大为500")
private Integer pageSize; private Long pageSize;
@Schema(description = "是否查询总条数") @Schema(description = "是否查询总条数")
protected Boolean searchCount; protected Boolean searchCount;

View File

@ -24,7 +24,7 @@ public class ResponseDTO<T> {
public static final int OK_CODE = 0; public static final int OK_CODE = 0;
public static final String OK_MSG = "success"; public static final String OK_MSG = "操作成功";
@Schema(description = "返回码") @Schema(description = "返回码")
private Integer code; private Integer code;

View File

@ -9,9 +9,7 @@ import net.lab1024.sa.base.constant.SwaggerTagConst;
import net.lab1024.sa.base.module.support.changelog.domain.form.ChangeLogQueryForm; import net.lab1024.sa.base.module.support.changelog.domain.form.ChangeLogQueryForm;
import net.lab1024.sa.base.module.support.changelog.domain.vo.ChangeLogVO; import net.lab1024.sa.base.module.support.changelog.domain.vo.ChangeLogVO;
import net.lab1024.sa.base.module.support.changelog.service.ChangeLogService; import net.lab1024.sa.base.module.support.changelog.service.ChangeLogService;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
@ -36,4 +34,11 @@ public class ChangeLogController extends SupportBaseController {
public ResponseDTO<PageResult<ChangeLogVO>> queryPage(@RequestBody @Valid ChangeLogQueryForm queryForm) { public ResponseDTO<PageResult<ChangeLogVO>> queryPage(@RequestBody @Valid ChangeLogQueryForm queryForm) {
return ResponseDTO.ok(changeLogService.queryPage(queryForm)); return ResponseDTO.ok(changeLogService.queryPage(queryForm));
} }
@Operation(summary = "变更内容详情 @author 卓大")
@GetMapping("/changeLog/getDetail/{changeLogId}")
public ResponseDTO<ChangeLogVO> getDetail(@PathVariable Long changeLogId) {
return ResponseDTO.ok(changeLogService.getById(changeLogId));
}
} }

View File

@ -33,7 +33,6 @@ public class ChangeLogService {
/** /**
* 分页查询 * 分页查询
*
*/ */
public PageResult<ChangeLogVO> queryPage(ChangeLogQueryForm queryForm) { public PageResult<ChangeLogVO> queryPage(ChangeLogQueryForm queryForm) {
Page<?> page = SmartPageUtil.convert2PageQuery(queryForm); Page<?> page = SmartPageUtil.convert2PageQuery(queryForm);
@ -57,7 +56,6 @@ public class ChangeLogService {
/** /**
* 更新 * 更新
*
*/ */
public synchronized ResponseDTO<String> update(ChangeLogUpdateForm updateForm) { public synchronized ResponseDTO<String> update(ChangeLogUpdateForm updateForm) {
ChangeLogEntity existVersion = changeLogDao.selectByVersion(updateForm.getVersion()); ChangeLogEntity existVersion = changeLogDao.selectByVersion(updateForm.getVersion());
@ -71,7 +69,6 @@ public class ChangeLogService {
/** /**
* 批量删除 * 批量删除
*
*/ */
public synchronized ResponseDTO<String> batchDelete(List<Long> idList) { public synchronized ResponseDTO<String> batchDelete(List<Long> idList) {
if (CollectionUtils.isEmpty(idList)) { if (CollectionUtils.isEmpty(idList)) {
@ -93,4 +90,8 @@ public class ChangeLogService {
changeLogDao.deleteById(changeLogId); changeLogDao.deleteById(changeLogId);
return ResponseDTO.ok(); return ResponseDTO.ok();
} }
public ChangeLogVO getById(Long changeLogId) {
return SmartBeanUtil.copy(changeLogDao.selectById(changeLogId), ChangeLogVO.class);
}
} }

View File

@ -87,7 +87,7 @@ public class CodeGeneratorController extends SupportBaseController {
ResponseDTO<byte[]> download = codeGeneratorService.download(tableName); ResponseDTO<byte[]> download = codeGeneratorService.download(tableName);
if (download.getOk()) { if (download.getOk()) {
SmartResponseUtil.setDownloadFileHeader(response, tableName + "-code.zip", (long) download.getData().length); SmartResponseUtil.setDownloadFileHeader(response, tableName + "_code.zip", (long) download.getData().length);
response.getOutputStream().write(download.getData()); response.getOutputStream().write(download.getData());
} else { } else {
SmartResponseUtil.write(response, download); SmartResponseUtil.write(response, download);

View File

@ -55,24 +55,15 @@ public class RepeatSubmitAspect {
if (StringUtils.isEmpty(ticket)) { if (StringUtils.isEmpty(ticket)) {
return point.proceed(); return point.proceed();
} }
Long timeStamp = this.repeatSubmitTicket.getTicketTimestamp(ticket); Long lastRequestTime = this.repeatSubmitTicket.getTicketTimestamp(ticket);
if (timeStamp != null) { if (lastRequestTime != null) {
Method method = ((MethodSignature) point.getSignature()).getMethod(); Method method = ((MethodSignature) point.getSignature()).getMethod();
RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class);
// 说明注解去掉了
if (annotation == null) {
return point.proceed();
}
int interval = Math.min(annotation.value(), RepeatSubmit.MAX_INTERVAL); int interval = Math.min(annotation.value(), RepeatSubmit.MAX_INTERVAL);
if (System.currentTimeMillis() < timeStamp + interval) { if (System.currentTimeMillis() < lastRequestTime + interval) {
// 续上时间 能在间隔时间内反复提示用户提交频繁
this.repeatSubmitTicket.putTicket(ticket);
// 提交频繁 // 提交频繁
return ResponseDTO.error(UserErrorCode.REPEAT_SUBMIT); return ResponseDTO.error(UserErrorCode.REPEAT_SUBMIT);
} }
} }
Object obj = null; Object obj = null;
try { try {

View File

@ -9,6 +9,7 @@ import net.lab1024.sa.base.common.domain.ResponseDTO;
import net.lab1024.sa.base.common.domain.PageResult; import net.lab1024.sa.base.common.domain.PageResult;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;

View File

@ -62,25 +62,9 @@ server:
basedir: ${project.log-directory}/tomcat-logs basedir: ${project.log-directory}/tomcat-logs
accesslog: accesslog:
enabled: true enabled: true
max-days: 7
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)" pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"
# 文件上传 配置
#file:
# storage:
# mode: local
# local:
# upload-path: /home/smart_admin_v3/upload/ #文件上传目录
# url-prefix:
# cloud:
# region: oss-cn-qingdao
# endpoint: oss-cn-qingdao.aliyuncs.com
# bucket-name: common
# access-key:
# secret-key:
# url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
# private-url-expire-seconds: 3600
# 文件上传 配置 # 文件上传 配置
file: file:
storage: storage:
@ -108,7 +92,7 @@ springdoc:
knife4j: knife4j:
enable: true enable: true
basic: basic:
enable: true enable: false
username: api # Basic认证用户名 username: api # Basic认证用户名
password: 1024 # Basic认证密码 password: 1024 # Basic认证密码

View File

@ -62,6 +62,7 @@ server:
basedir: ${project.log-directory}/tomcat-logs basedir: ${project.log-directory}/tomcat-logs
accesslog: accesslog:
enabled: true enabled: true
max-days: 7
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)" pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"

View File

@ -62,6 +62,7 @@ server:
basedir: ${project.log-directory}/tomcat-logs basedir: ${project.log-directory}/tomcat-logs
accesslog: accesslog:
enabled: true enabled: true
max-days: 30
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)" pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"

View File

@ -62,6 +62,7 @@ server:
basedir: ${project.log-directory}/tomcat-logs basedir: ${project.log-directory}/tomcat-logs
accesslog: accesslog:
enabled: true enabled: true
max-days: 7
pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)" pattern: "%t %{X-Forwarded-For}i %a %r %s (%D ms) %I (%B byte)"

View File

@ -7,10 +7,11 @@
--> -->
<template> <template>
<a-modal title="更新日志" width="700px" :open="visibleFlag" @cancel="onClose"> <a-modal title="更新日志" width="700px" :open="visibleFlag" @cancel="onClose">
<div> <div>
<pre>{{ content }}</pre> <pre>{{ content }}</pre>
<div v-if="link">链接<a :href="link" target="_blank">{{ link }}</a></div> <div v-if="link">
链接<a :href="link" target="_blank">{{ link }}</a>
</div>
</div> </div>
<template #footer> <template #footer>
@ -18,7 +19,6 @@
<a-button type="primary" @click="onClose">关闭</a-button> <a-button type="primary" @click="onClose">关闭</a-button>
</a-space> </a-space>
</template> </template>
</a-modal> </a-modal>
</template> </template>
<script setup> <script setup>

View File

@ -16,6 +16,8 @@
@close="onClose" @close="onClose"
destroyOnClose destroyOnClose
> >
<a-alert message="超管需要直接在数据库表 t_employee修改哦" type="error" closable />
<br />
<a-form ref="formRef" :model="form" :rules="rules" layout="vertical"> <a-form ref="formRef" :model="form" :rules="rules" layout="vertical">
<a-form-item label="姓名" name="actualName"> <a-form-item label="姓名" name="actualName">
<a-input v-model:value.trim="form.actualName" placeholder="请输入姓名" /> <a-input v-model:value.trim="form.actualName" placeholder="请输入姓名" />

View File

@ -57,6 +57,9 @@
bordered bordered
> >
<template #bodyCell="{ text, record, index, column }"> <template #bodyCell="{ text, record, index, column }">
<template v-if="column.dataIndex === 'administratorFlag'">
<a-tag color="error" v-if="text">超管</a-tag>
</template>
<template v-if="column.dataIndex === 'disabledFlag'"> <template v-if="column.dataIndex === 'disabledFlag'">
<a-tag :color="text ? 'error' : 'processing'">{{ text ? '禁用' : '启用' }}</a-tag> <a-tag :color="text ? 'error' : 'processing'">{{ text ? '禁用' : '启用' }}</a-tag>
</template> </template>
@ -152,6 +155,11 @@
dataIndex: 'loginName', dataIndex: 'loginName',
width: 100, width: 100,
}, },
{
title: '超管',
dataIndex: 'administratorFlag',
width: 60,
},
{ {
title: '状态', title: '状态',
dataIndex: 'disabledFlag', dataIndex: 'disabledFlag',

View File

@ -107,7 +107,7 @@
roleId: selectRoleId.value, roleId: selectRoleId.value,
dataScopeItemList: selectedDataScopeList.value.filter((e) => !_.isUndefined(e.viewType)), dataScopeItemList: selectedDataScopeList.value.filter((e) => !_.isUndefined(e.viewType)),
}; };
await roleApi.updateRoleDataScopeList(data); await roleApi.updateDataScope(data);
message.success('保存成功'); message.success('保存成功');
getDataScope(); getDataScope();
} catch (e) { } catch (e) {

View File

@ -0,0 +1,3 @@
NODE_ENV=development
VITE_APP_TITLE='SmartAdmin 开发环境(Dev)'
VITE_APP_API_URL='http://127.0.0.1:1024'

3
smart-app/.env.localhost Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV=development
VITE_APP_TITLE='SmartH5 本地环境(Local)'
VITE_APP_API_URL='http://127.0.0.1:1024'

3
smart-app/.env.pre Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV=production
VITE_APP_TITLE='SmartH5 预发布环境(Pre)'
VITE_APP_API_URL='https://app.smartadmin.vip/smart-app-api'

View File

@ -0,0 +1,3 @@
NODE_ENV=production
VITE_APP_TITLE='Smart App V3.X'
VITE_APP_API_URL='https://app.smartadmin.vip/smart-app-api'

3
smart-app/.env.test Normal file
View File

@ -0,0 +1,3 @@
NODE_ENV=production
VITE_APP_TITLE='SmartH5 测试环境(Test)'
VITE_APP_API_URL='http://127.0.0.1:1024'

18
smart-app/.eslintignore Normal file
View File

@ -0,0 +1,18 @@
*.sh
node_modules
lib
*.md
*.woff
*.ttf
.vscode
.idea
dist
public
/docs
.husky
.local
.localhost
/bin
Dockerfile
src/assets

66
smart-app/.eslintrc.cjs Normal file
View File

@ -0,0 +1,66 @@
/*
* @Description:
* @Author: zhuoda
* @Date: 2021-11-05
* @LastEditTime: 2022-07-05
* @LastEditors: zhuoda
*/
module.exports = {
root: true, //此项是用来告诉eslint找当前配置文件不能往父级查找
env: {
browser: true,
es2021: true,
node: true,
},
parser: 'vue-eslint-parser', //使用vue-eslint-parser 来解析vue文件中的 template和script
parserOptions: {
ecmaVersion: 12, // 默认情况下ESLint使用的是ECMAScript5语法此处我们设置的选项是 es12
sourceType: 'module', // 指定js导入的方式
},
extends: ['plugin:vue/vue3-essential', 'eslint:recommended', 'plugin:vue/base'],
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly',
},
plugins: ['vue'],
rules: {
'no-unused-vars': [
'error',
// we are only using this rule to check for unused arguments since TS
// catches unused variables but not args.
{ varsIgnorePattern: '.*', args: 'none' },
],
'space-before-function-paren': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/multi-word-component-names': [
'error',
{
ignores: ['index'], //需要忽略的组件名
},
],
'vue/html-self-closing': [
'error',
{
html: {
void: 'always',
normal: 'never',
component: 'always',
},
svg: 'always',
math: 'always',
},
],
// Enable vue/script-setup-uses-vars rule
'vue/script-setup-uses-vars': 'error',
},
};

21
smart-app/.gitignore vendored Normal file
View File

@ -0,0 +1,21 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

30
smart-app/.prettierrc.cjs Normal file
View File

@ -0,0 +1,30 @@
/*
* 代码格式化配置
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-12 14:44:18
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
module.exports = {
printWidth: 150, // 每行代码长度默认80
tabWidth: 2, // 缩进空格数
useTabs: false, //不用tab缩进
semi: true, //// 在语句末尾打印分号
singleQuote: true, // 使用单引号而不是双引号
vueIndentScriptAndStyle: true, //Vue文件脚本和样式标签缩进
quoteProps: 'as-needed', // 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
jsxSingleQuote: true, // 在JSX中使用单引号而不是双引号
trailingComma: 'es5', //多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>"默认none
bracketSpacing: true, // 在对象文字中的括号之间打印空格
jsxBracketSameLine: false, //jsx 标签的反尖括号需要换行
arrowParens: 'always', // 在单独的箭头函数参数周围包括括号 always(x) => x \ avoidx => x
rangeStart: 0, // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
rangeEnd: Infinity,
requirePragma: false, // 指定要使用的解析器,不需要写文件开头的 @prettier
insertPragma: false, // 不需要自动在文件开头插入 @prettier
proseWrap: 'preserve', // 使用默认的折行标准 always\never\preserve
htmlWhitespaceSensitivity: 'css', // 指定HTML文件的全局空格敏感度 css\strict\ignore
endOfLine: 'auto', // 因为prettier的规范和eslint的换行规则不同所以这个必须配置。要不然每次打开文件都会有一堆的警告;换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr
};

20
smart-app/index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

89
smart-app/package.json Normal file
View File

@ -0,0 +1,89 @@
{
"name": "smart-app",
"version": "3.0.0",
"author": {
"name": "1024创新实验室1024lab",
"email": "lab1024@163.com",
"url": "https://www.1024lab.net"
},
"license": "MIT",
"homepage": "https://smartadmin.1024lab.net",
"scripts": {
"dev:app": "uni -p app",
"dev:app-android": "uni -p app-android",
"dev:app-ios": "uni -p app-ios",
"dev:custom": "uni -p",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:mp-xhs": "uni -p mp-xhs",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:app": "uni build -p app",
"build:app-android": "uni build -p app-android",
"build:app-ios": "uni build -p app-ios",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-jd": "uni build -p mp-jd",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:mp-xhs": "uni build -p mp-xhs",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-3090920231225001",
"@dcloudio/uni-app-plus": "3.0.0-3090920231225001",
"@dcloudio/uni-components": "3.0.0-3090920231225001",
"@dcloudio/uni-h5": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-alipay": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-baidu": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-jd": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-lark": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-qq": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-toutiao": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-weixin": "3.0.0-3090920231225001",
"@dcloudio/uni-mp-xhs": "3.0.0-3090920231225001",
"@dcloudio/uni-quickapp-webview": "3.0.0-3090920231225001",
"@dcloudio/uni-ui": "1.5.0",
"crypto-js": "4.1.1",
"dayjs": "1.11.10",
"lodash": "4.17.21",
"pinia": "2.0.36",
"sm-crypto": "0.3.13",
"vue": "3.2.47",
"vue-i18n": "9.1.9"
},
"devDependencies": {
"@dcloudio/types": "3.3.2",
"@dcloudio/uni-automator": "3.0.0-3090920231225001",
"@dcloudio/uni-cli-shared": "3.0.0-3090920231225001",
"@dcloudio/uni-stacktracey": "3.0.0-3090920231225001",
"@dcloudio/vite-plugin-uni": "3.0.0-3090920231225001",
"@vue/runtime-core": "3.2.45",
"eslint": "8.16.0",
"eslint-config-prettier": "9.0.0",
"eslint-plugin-prettier": "5.0.0",
"eslint-plugin-vue": "9.17.0",
"prettier": "3.0.2",
"sass": "1.69.7",
"sass-loader": "10.1.1",
"vite": "4.0.3"
}
}

10
smart-app/shims-uni.d.ts vendored Normal file
View File

@ -0,0 +1,10 @@
/// <reference types='@dcloudio/types' />
import 'vue'
declare module '@vue/runtime-core' {
type Hooks = App.AppInstance & Page.PageInstance;
interface ComponentCustomOptions extends Hooks {
}
}

22
smart-app/src/App.vue Normal file
View File

@ -0,0 +1,22 @@
<script>
import { useUserStore } from '/@/store/modules/system/user';
export default {
onLaunch: function () {
useUserStore().getLoginInfo();
},
onShow: function () {
console.log('App Show');
},
onHide: function () {
console.log('App Hide');
},
};
</script>
<style lang="scss">
@import '@/uni_modules/uni-scss/index.scss';
/* 设置基准字体大小为16px */
body {
font-size: 16px;
}
</style>

View File

@ -0,0 +1,31 @@
/*
* @Description:
* @Author: zhuoda
* @Date: 2021-11-05
* @LastEditTime: 2022-06-23
* @LastEditors: zhuoda
*/
import { postRequest, getRequest } from '/@/lib/smart-request';
export const goodsApi = {
// 添加商品 @author zhuoda
addGoods: (param) => {
return postRequest('/goods/add', param);
},
// 删除 @author zhuoda
deleteGoods: (goodsId) => {
return getRequest(`/goods/delete/${goodsId}`);
},
// 批量 @author zhuoda
batchDelete: (goodsIdList) => {
return postRequest('/goods/batchDelete', goodsIdList);
},
// 分页查询 @author zhuoda
queryGoodsList: (param) => {
return postRequest('/goods/query', param);
},
// 更新商品 @author zhuoda
updateGoods: (param) => {
return postRequest('/goods/update', param);
},
};

View File

@ -0,0 +1,37 @@
/*
* 企业信息
*
* @Author: 开云
* @Date: 2023-09-03 21:47:28
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
import { postRequest, getRequest } from '/@/lib/smart-request';
export const enterpriseApi = {
// 新建企业 @author 开云
create: (param) => {
return postRequest('/oa/enterprise/create', param);
},
// 查询企业详情 @author 开云
detail: (enterpriseId) => {
return getRequest(`/oa/enterprise/get/${enterpriseId}`);
},
// 分页查询企业模块 @author 开云
pageQuery: (param) => {
return postRequest('/oa/enterprise/page/query', param);
},
// 编辑企业 @author 开云
update: (param) => {
return postRequest('/oa/enterprise/update', param);
},
// 删除企业 @author 开云
delete: (enterpriseId) => {
return getRequest(`/oa/enterprise/delete/${enterpriseId}`);
},
};

View File

@ -0,0 +1,33 @@
/*
* @Description: 公告信息企业动态
* @version:
* @Author: zhuoda
* @Date: 2022-08-16 20:34:36
*/
import { postRequest, getRequest } from '/@/lib/smart-request';
export const noticeApi = {
// ---------------- 通知公告类型 -----------------------
// 通知公告类型-获取全部 @author zhuoda
getAllNoticeTypeList() {
return getRequest('/oa/noticeType/getAll');
},
// --------------------- 【员工】查看 通知公告 -------------------------
// 通知公告-员工-查看详情 @author zhuoda
view(noticeId) {
return getRequest(`/oa/notice/employee/view/${noticeId}`);
},
// 通知公告-员工-查询 @author zhuoda
queryEmployeeNotice(param) {
return postRequest('/oa/notice/employee/query', param);
},
// 【员工】通知公告-查询 查看记录 @author zhuoda
queryViewRecord(param) {
return postRequest('/oa/notice/employee/queryViewRecord', param);
},
};

View File

@ -0,0 +1,24 @@
/**
* 系统更新日志 api 封装
*
* @Author: 卓大
* @Date: 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
import { postRequest, getRequest } from '/@/lib/smart-request';
export const changeLogApi = {
/**
* 分页查询 @author 卓大
*/
queryPage: (param) => {
return postRequest('/support/changeLog/queryPage', param);
},
/**
* 详情 @author 卓大
*/
getDetail: (changeLogId) => {
return getRequest(`/support/changeLog/getDetail/${changeLogId}`);
},
};

View File

@ -0,0 +1,59 @@
/*
* 字典
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-03 21:55:25
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
import { postRequest, getRequest } from '/@/lib/smart-request';
export const dictApi = {
// 分页查询数据字典KEY - @author 卓大
keyQuery: (param) => {
return postRequest('/support/dict/key/query', param);
},
// 查询全部字典key - @author 卓大
queryAllKey: () => {
return getRequest('/support/dict/key/queryAll');
},
/**
* 分页查询数据字典value - @author 卓大
*/
valueQuery: (param) => {
return postRequest('/support/dict/value/query', param);
},
// 数据字典KEY-添加- @author 卓大
keyAdd: (param) => {
return postRequest('/support/dict/key/add', param);
},
// 分页查询数据字典value - @author 卓大
valueAdd: (param) => {
return postRequest('/support/dict/value/add', param);
},
// 数据字典key-更新- @author 卓大
keyEdit: (param) => {
return postRequest('/support/dict/key/edit', param);
},
// 数据字典Value-更新- @author 卓大
valueEdit: (param) => {
return postRequest('/support/dict/value/edit', param);
},
// 数据字典key-删除- @author 卓大
keyDelete: (keyIdList) => {
return postRequest('/support/dict/key/delete', keyIdList);
},
// 数据字典Value-删除- @author 卓大
valueDelete: (valueIdList) => {
return postRequest('/support/dict/value/delete', valueIdList);
},
// 缓存刷新- @author 卓大
cacheRefresh: () => {
return getRequest('/support/dict/cache/refresh');
},
// 数据字典-值列表- @author 卓大
valueList: (keyCode) => {
return getRequest(`/support/dict/value/list/${keyCode}`);
},
};

View File

@ -0,0 +1,17 @@
/*
* 意见反馈
*
* @Author: 1024创新实验室开云
* @Date: 2022-09-03 21:56:31
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
import { postRequest } from '/src/lib/smart-request';
export const feedbackApi = {
// 意见反馈-新增
addFeedback: (params) => {
return postRequest('/support/feedback/add', params);
},
};

View File

@ -0,0 +1,14 @@
/**
* 系统更新日志 api 封装
*
* @Author: 卓大
* @Date: 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
import { uploadRequest } from '/@/lib/smart-request';
export const fileApi = {
upload: (file, folder) => {
return uploadRequest(file, folder);
},
};

View File

@ -0,0 +1,40 @@
/*
* 登录
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-03 21:59:58
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
import { getRequest, postRequest } from '/@/lib/smart-request';
export const loginApi = {
/**
* 登录 @author 卓大
*/
login: (param) => {
return postRequest('/login', param);
},
/**
* 退出登录 @author 卓大
*/
logout: () => {
return getRequest('/login/logout');
},
/**
* 获取验证码 @author 卓大
*/
getCaptcha: () => {
return getRequest('/login/getCaptcha');
},
/**
* 获取登录信息 @author 卓大
*/
getLoginInfo: () => {
return getRequest('/login/getLoginInfo');
},
};

View File

@ -0,0 +1,55 @@
<!---
* 字段 下拉选择框
*
* @Author: 1024创新实验室罗伊
* @Date: 2022-09-12 22:06:45
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*
-->
<template>
<div>
<uni-data-select :localdata="dictValueList" v-model="selectValue" :placeholder="props.placeholder" :clear="true" @change="onChange" />
</div>
</template>
<script setup>
import { onMounted, ref, watch } from 'vue';
import { dictApi } from '/@/api/support/dict-api';
const props = defineProps({
keyCode: String,
modelValue: [String],
placeholder: {
type: String,
default: '请选择',
},
});
// -------------------------- --------------------------
const dictValueList = ref([]);
async function queryDict() {
let res = await dictApi.valueList(props.keyCode);
dictValueList.value = res.data.map((e) => Object.assign({}, { text: e.valueName, value: e.valueCode }));
}
onMounted(queryDict);
// -------------------------- --------------------------
const selectValue = ref(props.modelValue);
watch(
() => props.modelValue,
(value) => {
selectValue.value = value;
}
);
const emit = defineEmits(['update:modelValue', 'change']);
function onChange(selectValue) {
let find = dictValueList.value.filter((e) => e.value === selectValue)[0];
emit('update:modelValue', find.value);
emit('change', find.value, find.text);
}
</script>

View File

@ -0,0 +1,70 @@
<template>
<view class="card">
<view class="card-header">
<slot name="title">
<view class="card-title">
{{ title }}
</view>
</slot>
<slot v-if="isExtra" name="extra" @click="extra">
<view class="card-extra">
查看更多>
</view>
</slot>
</view>
<view class="card-content">
<slot name="content"></slot>
</view>
</view>
</template>
<script setup>
const props = defineProps({
//
title:{
type:String,
default:''
},
//Extra
isExtra:{
type:Boolean,
default:true
}
})
const emits = defineEmits(['extra'])
const extra = ()=>{
emits('extra')
}
</script>
<style lang="scss">
.card{
width: 700rpx;
margin: 0 auto 20rpx;
border-radius: 12rpx;
overflow: hidden;
.card-header{
height: 76rpx;
background: linear-gradient(180deg,#e8f4ff, #f8fcff);
display: flex;
justify-content: space-between;
padding: 0 30rpx;
align-items: center;
.card-title{
font-size: 32rpx;
font-family: PingFang SC, PingFang SC-Semibold;
font-weight: 600;
text-align: left;
color: #323333;
}
.card-extra{
cursor: pointer;
font-size: 24rpx;
font-family: PingFang SC, PingFang SC-Regular;
font-weight: 400;
text-align: center;
color: #1a9aff;
}
}
}
</style>

View File

@ -0,0 +1,73 @@
<template>
<scroll-view class="scroll-view" scroll-x="true" :show-scrollbar="false">
<view class="item" :class="active === item.value ? 'active' : ''" v-for="item in tabsList" :key="item.value" @click="change(item.value)">
{{ item.label }}
</view>
</scroll-view>
</template>
<script setup>
import { ref, watch } from 'vue';
const props = defineProps({
modelValue: {
type: Number,
default: 0,
},
position: {
type: String,
default: 'fixed',
},
tabsList: {
type: Array,
default: [],
},
});
const position = ref(props.position);
const active = ref(props.modelValue);
watch(
() => props.modelValue,
(newValue) => {
active.value = newValue;
}
);
const emit = defineEmits(['update:modelValue', 'change']);
const change = (value) => {
active.value = value;
emit('update:modelValue', value);
emit('change', value);
};
</script>
<style lang="scss" scoped>
.scroll-view {
position: v-bind(position);
background-color: #f4f4f4;
z-index: 998;
:deep(::-webkit-scrollbar) {
height: 0 !important;
width: 0 !important;
background: transparent;
}
.item {
padding: 0 24rpx;
height: 72rpx;
font-size: 30rpx;
color: #777;
background: #fff;
display: inline-block;
border-radius: 8rpx;
line-height: 72rpx;
margin: 24rpx 0 24rpx 24rpx;
}
.active {
color: #ffffff;
background: #1a9aff;
}
}
</style>

View File

@ -0,0 +1,42 @@
<!--
* 枚举 radio
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-08-08 20:32:30
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*
-->
<template>
<radio-group @change="handleChange">
<label v-for="item in $smartEnumPlugin.getValueDescList(props.enumName)" :key="item.value" class="smart-margin-right10">
<radio :value="item.value + ''" :checked="item.value === modelValue">{{ item.desc }}</radio>
</label>
</radio-group>
</template>
<script setup>
import { ref, watch } from 'vue';
const props = defineProps({
enumName: String,
modelValue: [Number, String],
});
const emit = defineEmits(['update:modelValue', 'change']);
const selectValue = ref(props.modelValue);
watch(
() => props.modelValue,
(newValue) => {
selectValue.value = newValue;
}
);
function handleChange(e) {
emit('update:modelValue', e.detail.value);
emit('change', e.detail.value);
}
</script>

View File

@ -0,0 +1,49 @@
<!---
* 字段 下拉选择框
*
* @Author: 1024创新实验室罗伊
* @Date: 2022-09-12 22:06:45
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*
-->
<template>
<div>
<uni-data-select :localdata="dataList" v-model="selectValue" :clear="true" @change="onChange" />
</div>
</template>
<script setup>
import { inject, onMounted, ref, watch } from 'vue';
const smartEnumPlugin = inject('smartEnumPlugin');
const props = defineProps({
enumName: String,
modelValue: [Number, String],
});
// -------------------------- --------------------------
const dataList = ref([]);
function getEnumData() {
dataList.value = smartEnumPlugin.getValueDescList(props.enumName).map((e) => Object.assign({}, { text: e.desc, value: e.value }));
}
onMounted(getEnumData);
// -------------------------- --------------------------
const selectValue = ref(props.modelValue);
watch(
() => props.modelValue,
(value) => {
selectValue.value = value;
}
);
const emit = defineEmits(['update:modelValue', 'change']);
function onChange(value) {
emit('update:modelValue', value);
emit('change', value, smartEnumPlugin.getDescByValue(props.enumName, value));
}
</script>

View File

@ -0,0 +1,26 @@
/*
* 商品
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-03 22:08:10
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
export const GOODS_STATUS_ENUM = {
APPOINTMENT: {
value: 1,
desc: '预约中',
},
SELL: {
value: 2,
desc: '售卖中',
},
SELL_OUT: {
value: 3,
desc: '售罄',
},
};
export default {
GOODS_STATUS_ENUM,
};

View File

@ -0,0 +1,24 @@
/*
* 企业
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2023-09-03 22:07:27
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
export const ENTERPRISE_TYPE_ENUM = {
NORMAL: {
value: 1,
desc: '有限企业',
},
FOREIGN: {
value: 2,
desc: '外资企业',
},
};
export default {
ENTERPRISE_TYPE_ENUM,
};

View File

@ -0,0 +1,70 @@
/*
* 通用常量
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 19:57:29
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
export const PAGE_SIZE = 10;
export const PAGE_SIZE_OPTIONS = ['5', '10', '15', '20', '30', '40', '50', '75', '100', '150', '200', '300', '500'];
//登录页面名字
export const PAGE_PATH_LOGIN = '/login';
//404页面名字
export const PAGE_PATH_404 = '/404';
export const showTableTotal = function (total) {
return `${total}`;
};
export const FLAG_NUMBER_ENUM = {
TRUE: {
value: 1,
desc: '是',
},
FALSE: {
value: 0,
desc: '否',
},
};
export const GENDER_ENUM = {
UNKNOWN: {
value: 0,
desc: '未知',
},
MAN: {
value: 1,
desc: '男',
},
WOMAN: {
value: 2,
desc: '女',
},
};
export const USER_TYPE_ENUM = {
ADMIN_EMPLOYEE: {
value: 1,
desc: '员工',
},
};
export const DATA_TYPE_ENUM = {
NORMAL: {
value: 1,
desc: '普通',
},
ENCRYPT: {
value: 10,
desc: '加密',
},
};

View File

@ -0,0 +1,26 @@
/*
* 所有常量入口
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 19:58:28
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
import { FLAG_NUMBER_ENUM, GENDER_ENUM, USER_TYPE_ENUM } from './common-const';
import loginDevice from './system/login-device-const';
import enterpriseConst from './business/oa/enterprise-const';
import goodsConst from './business/erp/goods-const';
import changeLogConst from './support/change-log-const';
import fileConst from './support/file-const';
export default {
FLAG_NUMBER_ENUM,
GENDER_ENUM,
USER_TYPE_ENUM,
...loginDevice,
...enterpriseConst,
...goodsConst,
...changeLogConst,
...fileConst,
};

View File

@ -0,0 +1,19 @@
/*
* key 常量
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 19:58:50
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
/**
* key前缀
*/
const KEY_PREFIX = 'smart_h5_';
/**
* localStorageKey集合
*/
// token
export const USER_TOKEN = `${KEY_PREFIX}token`;

View File

@ -0,0 +1,28 @@
/*
* 正则常量
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 19:59:05
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
export const regular = {
phone: /^(13|14|15|16|17|18|19)\d{9}$/,
qq: /^[1-9]\d{3,}$/,
linkUrl:
/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/,
// eslint-disable-next-line no-useless-escape
isNumber: /(^[\-1-9][1-9]*(.[1-9]+)?)$/, // 判断是否为数字除了0 外
isLandlineOrPhone: /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/, // 验证 座机 或者手机
account: /^[a-z0-9]{3,16}$/, // 请输入3-16位(小写字母|数字)的账号
mobileAccount: /^[a-z0-9]{6,16}$/, // 请输入6-16位(小写字母|数字)的账号(和移动端保持一致)
accountDesc: '请输入3-16位(小写字母|数字)的账号',
pwd: /^[A-Za-z0-9._]{6,16}$/, // 请输入6-16位(大小写字母|数字|小数点|下划线)的密码
pwdDesc: '请输入6-16位(大小写字母|数字|小数点|下划线)的密码',
delBlankSpace: /\s+/g, // 删除空格
isPdfReg: new RegExp(/\.(pdf|PDF)/),
isElseFileReg: new RegExp(/\.(doc|docx|xls|xlsx|txt|ppt|pptx|pps|ppxs)/),
isIdentityCard: /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X|x)$/, // 验证身份证号
isChinese: /^[\u4e00-\u9fa5]+$/gi, // 验证是否汉字
};

View File

@ -0,0 +1,32 @@
/**
* 系统更新日志 枚举
*
* @Author: 卓大
* @Date: 2022-09-26 14:53:50
* @Copyright 1024创新实验室
*/
/**
* 更新类型:[1:特大版本功能更新;2:功能更新;3:bug修复]
*/
export const CHANGE_LOG_TYPE_ENUM = {
MAJOR_UPDATE: {
value: 1,
desc: '重大更新',
type: 'error',
},
FUNCTION_UPDATE: {
value: 2,
desc: '功能更新',
type: 'primary',
},
BUG_FIX: {
value: 3,
desc: 'Bug修复',
type: 'warning',
},
};
export default {
CHANGE_LOG_TYPE_ENUM,
};

View File

@ -0,0 +1,31 @@
/*
* 文件类型
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-03 22:09:10
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
// 文件上传类型
export const FILE_FOLDER_TYPE_ENUM = {
COMMON: {
value: 1,
desc: '通用',
},
NOTICE: {
value: 2,
desc: '公告',
},
HELP_DOC: {
value: 3,
desc: '帮助中心',
},
FEEDBACK: {
value: 4,
desc: '意见反馈',
},
};
export default {
FILE_FOLDER_TYPE_ENUM,
};

View File

@ -0,0 +1,31 @@
/*
* 登录设备
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 19:56:56
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
export const LOGIN_DEVICE_ENUM = {
PC: {
value: 1,
desc: '电脑端',
},
ANDROID: {
value: 2,
desc: '安卓',
},
APPLE: {
value: 3,
desc: '苹果',
},
H5: {
value: 3,
desc: 'H5',
},
};
export default {
LOGIN_DEVICE_ENUM,
};

View File

@ -0,0 +1,87 @@
import CryptoJS from 'crypto-js';
import CryptoSM from 'sm-crypto';
function object2string(data) {
if (typeof data === 'Object') {
return JSON.stringify(data);
}
let str = JSON.stringify(data);
if (str.startsWith("'") || str.startsWith('"')) {
str = str.substring(1);
}
if (str.endsWith("'") || str.endsWith('"')) {
str = str.substring(0, str.length - 1);
}
return str;
}
// ----------------------- AES 加密、解密 -----------------------
const AES_KEY = '1024abcd1024abcd1024abcd1024abcd';
const AES = {
encryptData: function (data) {
// AES 加密 并转为 base64
let utf8Data = CryptoJS.enc.Utf8.parse(object2string(data));
const key = CryptoJS.enc.Utf8.parse(AES_KEY);
const encrypted = CryptoJS.AES.encrypt(utf8Data, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
});
return encrypted.toString();
},
decryptData: function (data) {
// 第一步Base64 解码
let words = CryptoJS.enc.Base64.parse(data);
// 第二步AES 解密
const key = CryptoJS.enc.Utf8.parse(AES_KEY);
return CryptoJS.AES.decrypt({ ciphertext: words }, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
}).toString(CryptoJS.enc.Utf8);
},
};
// ----------------------- 国密SM4算法 加密、解密 -----------------------
const SM4_KEY = '1024abcd1024abcd1024abcd1024abcd';
const SM4 = {
encryptData: function (data) {
// 第一步SM4 加密
let encryptData = CryptoSM.sm4.encrypt(object2string(data), SM4_KEY);
// 第二步: Base64 编码
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encryptData));
},
decryptData: function (data) {
// 第一步Base64 解码
let words = CryptoJS.enc.Base64.parse(data);
let decode64Str = CryptoJS.enc.Utf8.stringify(words);
// 第二步SM4 解密
return CryptoSM.sm4.decrypt(decode64Str, SM4_KEY);
},
};
// ----------------------- 对外暴露: 加密、解密 -----------------------
// 默认使用SM4算法
const EncryptObject = SM4;
// const EncryptObject = AES;
/**
* 加密
*/
export const encryptData = function (data) {
return !data ? null : EncryptObject.encryptData(data);
};
/**
* 解密
*/
export const decryptData = function (data) {
return !data ? null : EncryptObject.decryptData(data);
};

View File

@ -0,0 +1,128 @@
/*
* ajax请求
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2022-09-06 20:46:03
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
import { USER_TOKEN } from '/@/constants/local-storage-key-const';
import { DATA_TYPE_ENUM } from '/@/constants/common-const';
import { decryptData, encryptData } from './encrypt';
import { useUserStore } from '/@/store/modules/system/user';
const baseUrl = import.meta.env.VITE_APP_API_URL;
function getUserToken() {
let token = uni.getStorageSync(USER_TOKEN);
if (token) {
return token;
}
return '';
}
/**
* 处理返回的消息
*/
function handleResponse(response, resolve, reject) {
// 如果是加密数据
if (response.data.dataType === DATA_TYPE_ENUM.ENCRYPT.value) {
response.data.encryptData = response.data.data;
let decryptStr = decryptData(response.data.data);
if (decryptStr) {
response.data.data = JSON.parse(decryptStr);
}
}
const res = response.data;
if (res.code && res.code !== 1) {
// `token` 过期或者账号已在别处登录
if (res.code === 30007 || res.code === 30008 || res.code === 30012) {
uni.showToast({
title: res.msg,
icon: 'none',
});
useUserStore().clearUserLoginInfo();
uni.navigateTo({ url: '/pages/login/login' });
}
uni.showToast({
title: res.msg,
icon: 'none',
});
reject(response);
} else {
resolve(res);
}
}
/**
* 通用请求封装
*/
export const request = function (url, method, data) {
return new Promise((resolve, reject) => {
uni.request({
url: baseUrl + url, //拼接请求路径
data: data,
method: method,
header: {
'x-access-token': getUserToken(),
},
success: (response) => {
handleResponse(response, resolve, reject);
},
fail: (error) => {
reject(error);
},
});
});
};
/**
* get请求
*/
export const getRequest = (url) => {
return request(url, 'GET');
};
/**
* post请求
*/
export const postRequest = (url, data) => {
return request(url, 'POST', data);
};
// ================================= 加密 =================================
/**
* 加密请求参数的post请求
*/
export const postEncryptRequest = (url, data) => {
return request(url, 'POST', { encryptData: encryptData(data) });
};
// ================================= 文件 =================================
export const uploadRequest = function (filePath, folder) {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: baseUrl + '/support/file/upload',
filePath,
header: {
'x-access-token': getUserToken(),
},
name: 'file',
formData: {
folder,
},
success: (response) => {
response.data = JSON.parse(response.data.replace('\uFEFF', ''));
handleResponse(response, resolve, reject);
},
fail: (error) => {
reject(error);
},
});
});
};

View File

@ -0,0 +1,22 @@
/*
* 错误上报sentry
*
* @Author: 1024创新实验室-主任卓大
* @Date: 2024-01-02 20:49:28
* @Wechat: zhuda1024
* @Email: lab1024@163.com
* @Copyright 1024创新实验室 https://1024lab.net Since 2012
*/
export const smartSentry = {
/**
* sentry 主动上报
*/
captureError: (error) => {
if (error.config && error.data && error && error.headers && error.request && error.status) {
return;
}
// Sentry.captureException(error);
console.error(error);
},
};

View File

@ -0,0 +1,30 @@
export const SmartLoading = {
show: function (msg) {
uni.showLoading({ title: msg ? msg : '加载中' });
},
hide: function () {
uni.hideLoading();
},
};
export const SmartToast = {
success: (message) => {
uni.showToast({
title: message,
icon: 'success',
});
},
error: (message) => {
uni.showToast({
title: message,
icon: 'error',
});
},
toast: (message) => {
uni.showToast({
title: message,
icon: 'none',
});
},
};

22
smart-app/src/main.js Normal file
View File

@ -0,0 +1,22 @@
import { createSSRApp } from 'vue';
import App from './App.vue';
import { store } from './store/index';
/*每个页面公共css */
import './theme/index.scss';
// 枚举管理
import smartEnumPlugin from '/@/plugins/smart-enums-plugin';
import constantsInfo from '/@/constants/index';
import lodash from 'lodash';
export function createApp() {
const app = createSSRApp(App);
app.use(store);
app.use(smartEnumPlugin, constantsInfo);
app.config.globalProperties.$lodash = lodash;
return {
app,
store,
};
}

View File

@ -0,0 +1,72 @@
{
"name" : "",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics": {
"enable": false
},
"vueVersion" : "3"
}

212
smart-app/src/pages.json Normal file
View File

@ -0,0 +1,212 @@
{
"easycom": {
"autoscan": true,
"custom": {
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
"^y-(.*)": "@/uni_modules/y-$1/components/y-$1.vue"
}
},
"pages": [
{
"path": "pages/home/index",
"style": {
"navigationStyle":"custom",
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "登录",
"navigationBarTextStyle": "white",
"navigationStyle": "custom"
}
},
{
"path" : "pages/mine/mine",
"style" :
{
"navigationBarTitleText" : "我的",
"enablePullDownRefresh" : false,
"navigationStyle": "custom"
}
},
{
"path" : "pages/enterprise/enterprise-list",
"style" :
{
"navigationBarTitleText" : "客户线索",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/enterprise/enterprise-form",
"style" :
{
"navigationBarTitleText" : "添加客户",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/enterprise/enterprise-detail",
"style" :
{
"navigationBarTitleText" : "客户详情",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/notice/notice-index",
"style" :
{
"navigationBarTitleText" : "通知公告",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/notice/notice-detail",
"style" :
{
"navigationBarTitleText" : "公告内容",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/goods/goods-index",
"style" :
{
"navigationBarTitleText" : "商品",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/support/change-log/change-log-list",
"style" :
{
"navigationBarTitleText" : "版本更新",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/support/change-log/change-log-detail",
"style" :
{
"navigationBarTitleText" : "版本更新内容",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/message/message",
"style" :
{
"navigationBarTitleText" : "消息",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/form/form",
"style" :
{
"navigationBarTitleText" : "提交表单",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/select-people/select-people",
"style" :
{
"navigationBarTitleText" : "选择人员",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/list/list",
"style" :
{
"navigationBarTitleText" : "常见列表样式1",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/list2/list",
"style" :
{
"navigationBarTitleText" : "常见列表样式2",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/order-detail/order-detail",
"style" :
{
"navigationBarTitleText" : "运单详情",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
},
{
"path" : "pages/support/feedback/feedback-form",
"style" :
{
"navigationBarTitleText" : "意见反馈",
"enablePullDownRefresh" : false,
"navigationBarBackgroundColor": "#fff"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
},
"tabBar": {
"color": "#858585",
"selectedColor": "#1A9AFF",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/home/index",
"iconPath": "static/images/tabbar/home-icon.png",
"selectedIconPath": "static/images/tabbar/home-icon-h.png",
"text": "首页"
},
{
"pagePath": "pages/list/list",
"iconPath": "static/images/tabbar/list-icon.png",
"selectedIconPath": "static/images/tabbar/list-icon-h.png",
"text": "常见列表1"
},
{
"pagePath": "pages/list2/list",
"iconPath": "static/images/tabbar/list-icon.png",
"selectedIconPath": "static/images/tabbar/list-icon-h.png",
"text": "常见列表2"
},
{
"pagePath": "pages/message/message",
"iconPath": "static/images/tabbar/message-icon.png",
"selectedIconPath": "static/images/tabbar/message-icon-h.png",
"text": "消息"
},
{
"pagePath": "pages/mine/mine",
"iconPath": "static/images/tabbar/mine-icon.png",
"selectedIconPath": "static/images/tabbar/mine-icon-h.png",
"text": "我的"
}]
}
}

View File

@ -0,0 +1,245 @@
<template>
<view class="container">
<view>
<smart-detail-tabs :tabsList="tabs" v-model="smartTabIndex" @change="onTabChange" :fixed="true" />
</view>
<view class="smart-detail smart-margin-top60 content" id="detail1">
<view class="smart-detail-card">
<view class="smart-detail-card-title"> 企业基础信息</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 企业名称 </view>
<view class="smart-detail-card-value">
{{ data.enterpriseName }}
</view>
</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 统一社会信用代码 </view>
<view class="smart-detail-card-value">
{{ data.unifiedSocialCreditCode }}
</view>
</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 类型 </view>
<view class="smart-detail-card-value">
{{ $smartEnumPlugin.getDescByValue('ENTERPRISE_TYPE_ENUM', data.type) }}
</view>
</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 企业ID </view>
<view class="smart-detail-card-value">
{{ data.enterpriseId }}
</view>
</view>
</view>
<view class="smart-detail-card" id="detail2">
<view class="smart-detail-card-title"> 图片信息</view>
<view class="smart-detail-card-cell" v-if="data.enterpriseLogo && data.enterpriseLogo.length > 0">
<view class="smart-detail-card-label"> 企业logo </view>
<view class="smart-detail-card-value" @click="preview([data.enterpriseLogo[0].fileUrl])">
<image :src="data.enterpriseLogo[0].fileUrl"></image>
</view>
</view>
<view class="smart-detail-card-cell" v-if="data.businessLicense && data.businessLicense.length > 0">
<view class="smart-detail-card-label"> 营业执照 </view>
<view class="smart-detail-card-value" @click="preview([data.businessLicense[0].fileUrl])">
<image :src="data.businessLicense[0].fileUrl"></image>
</view>
</view>
</view>
<view class="smart-detail-card" id="detail3">
<view class="smart-detail-card-title"> 联系方式</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 联系人 </view>
<view class="smart-detail-card-value">
{{ data.contact }}
</view>
</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 联系人电话 </view>
<view class="smart-detail-card-value">
{{ data.contactPhone }}
</view>
</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 邮箱 </view>
<view class="smart-detail-card-value">
{{ data.email }}
</view>
</view>
</view>
</view>
<view class="btn-group">
<button class="btn" type="warn" @click="onDelete">删除</button>
<button class="btn" type="primary" @click="onChange">修改</button>
</view>
</view>
</template>
<script setup>
import SmartDetailTabs from '/@/components/smart-detail-tabs/index.vue';
import { ref, reactive } from 'vue';
import { enterpriseApi } from '/@/api/business/oa/enterprise-api';
import { onShow, onLoad } from '@dcloudio/uni-app';
import { smartSentry } from '/@/lib/smart-sentry';
import { SmartLoading, SmartToast } from '/@/lib/smart-support';
// ----------------------- tab -----------------------
const tabs = ref([
{
label: '基础信息',
value: 1,
},
{
label: '图片信息',
value: 2,
},
{
label: '联系人',
value: 3,
},
]);
const smartTabIndex = ref(1);
function onTabChange(tabIndex) {
console.log(12, tabIndex);
uni.pageScrollTo({
selector: '#detail' + tabIndex,
duration: 300,
success: console.log,
fail: console.log,
});
}
// ----------------------- -----------------------
const data = reactive({
//ID
enterpriseId: '',
//
enterpriseName: '',
//
unifiedSocialCreditCode: '',
//
type: '',
//
contact: '',
//
contactPhone: '',
//
email: '',
//
address: '',
//logo
enterpriseLogo: undefined,
//
businessLicense: undefined,
});
async function getDetail(id) {
try {
SmartLoading.show();
let res = await enterpriseApi.detail(id);
data.enterpriseId = res.data.enterpriseId;
data.enterpriseName = res.data.enterpriseName;
data.unifiedSocialCreditCode = res.data.unifiedSocialCreditCode;
data.type = res.data.type;
data.contact = res.data.contact;
data.contactPhone = res.data.contactPhone;
data.email = res.data.email;
data.address = res.data.address;
data.enterpriseLogo = res.data.enterpriseLogo;
data.businessLicense = res.data.businessLicense;
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
function preview(urls) {
uni.previewImage({
urls: urls,
});
}
onLoad((options) => {
if (options.enterpriseId) {
data.enterpriseId = options.enterpriseId;
getDetail(options.enterpriseId);
}
});
onShow(() => {
getDetail(data.enterpriseId);
});
function onChange() {
uni.navigateTo({ url: '/pages/enterprise/enterprise-form?enterpriseId=' + data.enterpriseId });
}
function onDelete() {
uni.showModal({
title: '提示',
content: '确定要删除吗?',
confirmText: '确定删除',
success: function (res) {
if (res.confirm) {
doDelete();
}
},
});
}
async function doDelete() {
try {
SmartLoading.show();
await enterpriseApi.delete(data.enterpriseId);
SmartToast.success('删除成功');
setTimeout(() => {
uni.redirectTo({ url: '/pages/enterprise/enterprise-list' });
}, 500);
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #f4f4f4;
height: 100vh;
overflow-y: scroll;
}
.content {
margin-bottom: 120px;
}
.btn-group {
border-top: #eee 1px solid;
height: 80px;
display: flex;
flex-direction: row;
align-items: center;
position: fixed;
bottom: 0;
background-color: white;
width: 100%;
.btn {
margin: 10px;
flex: 1;
}
}
</style>

View File

@ -0,0 +1,184 @@
<template>
<view class="container">
<view class="smart-form">
<uni-forms ref="formRef" :label-width="100" :modelValue="form" label-position="left" :rules="rules">
<view class="smart-form-group">
<view class="smart-form-group-title"> 基本信息 </view>
<view class="smart-form-group-content">
<uni-forms-item class="smart-form-item" label="企业名称:" name="enterpriseName" required>
<uni-easyinput class="uni-mt-5" trim="all" v-model="form.enterpriseName" placeholder="请输入 企业名称" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="统一社会信用代码:" name="unifiedSocialCreditCode" required>
<uni-easyinput class="uni-mt-5" trim="all" v-model="form.unifiedSocialCreditCode" placeholder="请输入 统一社会信用代码" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="企业类型:" name="type" required>
<smart-enum-radio v-model="form.type" enumName="ENTERPRISE_TYPE_ENUM" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="公司地址:" name="address">
<uni-easyinput class="uni-mt-5" trim="all" v-model="form.address" placeholder="请输入 公司地址" />
</uni-forms-item>
</view>
</view>
<view class="smart-form-group">
<view class="smart-form-group-title"> 联系方式 </view>
<view class="smart-form-group-content">
<uni-forms-item class="smart-form-item" label="联系人" name="contact" required>
<uni-easyinput class="uni-mt-5" trim="all" v-model="form.contact" placeholder="请输入 联系人" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="联系人电话" name="contactPhone" required>
<uni-easyinput class="uni-mt-5" trim="all" v-model="form.contactPhone" placeholder="请输入 联系人电话" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="邮箱" name="email">
<uni-easyinput class="uni-mt-5" trim="all" v-model="form.email" placeholder="请输入 邮箱" />
</uni-forms-item>
</view>
</view>
</uni-forms>
<view class="smart-form-submit smart-margin-top20 bottom-button">
<button class="smart-form-submit-btn smart-margin-right20" type="default" @click="cancel">取消</button>
<button class="smart-form-submit-btn" type="warn" @click="reset">重置</button>
<button class="smart-form-submit-btn" type="primary" @click="ok">保存</button>
</view>
</view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { enterpriseApi } from '/@/api/business/oa/enterprise-api';
import { regular } from '/@/constants/regular-const';
import SmartEnumRadio from '/@/components/smart-enum-radio/index.vue';
import { smartSentry } from '/@/lib/smart-sentry';
import { SmartLoading, SmartToast } from '/@/lib/smart-support';
import { onLoad, onReady, onShow } from '@dcloudio/uni-app';
// --------------------- ---------------------
const defaultFormData = {
enterpriseId: undefined,
enterpriseName: undefined,
unifiedSocialCreditCode: undefined,
type: undefined,
contact: undefined,
contactPhone: undefined,
email: undefined,
address: undefined,
disabledFlag: false,
};
let form = reactive({ ...defaultFormData });
const rules = {
enterpriseName: {
rules: [{ required: true, errorMessage: '请输入企业名称' }],
},
unifiedSocialCreditCode: {
rules: [{ required: true, errorMessage: '请输入 统一社会信用代码' }],
},
contact: {
rules: [{ required: true, errorMessage: '请输入 联系人' }],
},
contactPhone: {
rules: [
{ required: true, errorMessage: '请输入联系人电话' },
{ pattern: regular.phone, errorMessage: '电话格式错误' },
],
},
type: {
rules: [{ required: true, errorMessage: '请选择 类型' }],
},
};
// --------------------- ---------------------
onLoad((options) => {
if (options.enterpriseId) {
form.enterpriseId = options.enterpriseId;
getDetail(options.enterpriseId);
}
});
onReady(() => {
if (form.enterpriseId) {
uni.setNavigationBarTitle({
title: '修改客户',
});
}
});
async function getDetail(id) {
try {
SmartLoading.show();
let res = await enterpriseApi.detail(id);
form.enterpriseId = res.data.enterpriseId;
form.enterpriseName = res.data.enterpriseName;
form.unifiedSocialCreditCode = res.data.unifiedSocialCreditCode;
form.type = res.data.type;
form.contact = res.data.contact;
form.contactPhone = res.data.contactPhone;
form.email = res.data.email;
form.address = res.data.address;
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
// ----------------------- ------------------------
const formRef = ref();
//
function cancel() {
close();
uni.navigateBack();
}
//
function reset() {
Object.assign(form, defaultFormData);
formRef.value.clearValidate();
}
//
function ok() {
formRef.value
.validate()
.then(async () => {
SmartLoading.show();
try {
if (form.enterpriseId) {
await enterpriseApi.update(form);
} else {
await enterpriseApi.create(form);
}
SmartToast.success(`${form.enterpriseId ? '修改' : '添加'}成功`);
uni.navigateBack();
} catch (error) {
smartSentry.captureError(error);
} finally {
SmartLoading.hide();
}
})
.catch((error) => {
console.log('error', error);
SmartToast.toast('参数验证错误,请仔细填写表单数据!');
});
}
</script>
<style lang="scss" scoped>
.query-form-pop {
height: 800rpx;
overflow-y: scroll;
}
.bottom-button {
position: fixed;
bottom: 0;
}
</style>

View File

@ -0,0 +1,217 @@
<template>
<view class="container">
<mescroll-body @init="mescrollInit" :down="{ auto: false }" @down="onDown" @up="onUp" :up="{ toTop: { src: '' } }">
<!--搜索框-->
<uni-nav-bar :border="false" fixed :leftWidth="0" rightWidth="70px">
<view class="input">
<uni-easyinput
prefixIcon="search"
:clearable="true"
trim="all"
v-model="queryForm.keywords"
placeholder="搜索:企业名称、联系人、联系电话等"
@confirm="search"
@clear="search"
/>
</view>
<template #right>
<view class="nav-right" @click="search">
<uni-icons type="search" size="30"></uni-icons>
<view class="nav-right-name"> 搜索 </view>
</view>
</template>
</uni-nav-bar>
<!-- 列表 -->
<view class="list-container">
<view class="list-item" @click="gotoDetail(item.enterpriseId)" v-for="item in listData" :key="item.enterpriseId">
<view class="list-item-row">
<view class="list-item-label">公司</view>
<view class="list-item-content bolder">{{ item.enterpriseName }}</view>
<view class="list-item-phone" @click="callPhone(item.contactPhone)">
<uni-icons type="phone" size="18" color="#007aff"></uni-icons>
联系
</view>
</view>
<view class="list-item-row">
<view class="list-item-label">营业执照</view>
<view class="list-item-content">{{ item.unifiedSocialCreditCode }}</view>
</view>
<view class="list-item-row">
<view class="list-item-label">联系人</view>
<view class="list-item-content">{{ item.contact }}</view>
</view>
<view class="list-item-row">
<view class="list-item-label">电话</view>
<view class="list-item-content">{{ item.contactPhone }}</view>
</view>
</view>
</view>
</mescroll-body>
<uni-fab ref="fab" :pattern="fabPattern" horizontal="right" @fabClick="gotoAdd" />
</view>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { enterpriseApi } from '/@/api/business/oa/enterprise-api';
import { onPageScroll, onReachBottom, onShow } from '@dcloudio/uni-app';
import useMescroll from '@/uni_modules/uni-mescroll/hooks/useMescroll';
import { smartSentry } from '/@/lib/smart-sentry';
import _ from 'lodash';
// --------------------------- ---------------------------------
const defaultForm = {
keywords: '', //
pageNum: 1,
pageSize: 10,
};
//
const queryForm = reactive({ ...defaultForm });
//
const listData = ref([]);
function buildQueryParam(pageNum) {
queryForm.pageNum = pageNum;
return Object.assign({}, queryForm, { pageNum });
}
async function query(mescroll, isDownFlag, param) {
try {
let res = await enterpriseApi.pageQuery(param);
if (!isDownFlag) {
listData.value = listData.value.concat(res.data.list);
} else {
listData.value = res.data.list;
}
mescroll.endSuccess(res.data.list.length, res.data.pages > res.data.pageNum);
} catch (e) {
smartSentry.captureError(e);
//,
mescroll.endErr();
}
}
const { mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
/**
* 搜索
*/
function search() {
query(getMescroll(), true, buildQueryParam(1));
uni.pageScrollTo({
scrollTop: 0,
});
}
/**
* 下拉刷新
*/
function onDown(mescroll) {
queryForm.pageNum = 1;
query(mescroll, true, buildQueryParam(1));
}
/**
* 上拉加载更多
*/
function onUp(mescroll) {
query(mescroll, false, buildQueryParam(mescroll.num));
}
onShow(() => {
search();
});
// --------------------------- ---------------------------------
const fabPattern = reactive({
color: 'blue',
backgroundColor: 'blue',
});
function gotoAdd() {
uni.navigateTo({ url: '/pages/enterprise/enterprise-form' });
}
// --------------------------- ---------------------------------
function callPhone(phone) {
uni.makePhoneCall({
phoneNumber: phone, //
});
}
function gotoDetail(id) {
uni.navigateTo({ url: '/pages/enterprise/enterprise-detail?enterpriseId=' + id });
}
</script>
<style lang="scss" scoped>
.container {
background-color: #f4f4f4;
}
.input {
width: 100%;
height: 60rpx;
background: #f7f8f9;
border-radius: 4px;
margin: 8rpx 0;
display: flex;
align-items: center;
}
.nav-right {
width: 140rpx;
display: flex;
height: 88rpx;
flex-direction: row;
line-height: 88rpx;
.nav-right-name {
margin-left: 5px;
line-height: 88rpx;
font-size: 30rpx;
}
}
.list-container {
padding: 10rpx 20rpx;
margin-top: 10rpx;
.list-item {
background: #ffffff;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
margin-bottom: 20rpx;
padding: 30rpx 40rpx;
.list-item-row {
display: flex;
flex-direction: row;
margin-bottom: 16rpx;
justify-content: flex-start;
.list-item-label {
font-size: 28rpx;
font-weight: 400;
text-align: left;
color: #999999;
}
.bolder {
font-weight: 600 !important;
}
.list-item-content {
font-size: 30rpx;
font-weight: 500;
text-align: left;
color: #323333;
}
.list-item-phone {
color: $uni-color-primary;
margin-left: auto;
}
}
}
}
</style>

View File

@ -0,0 +1,94 @@
<template>
<view class="smart-form">
<uni-forms :label-width="100" :modelValue="formData" label-position="left">
<view class="smart-form-group">
<view class="smart-form-group-title"> 常用功能 </view>
<view class="smart-form-group-content">
<uni-forms-item class="smart-form-item" label="姓名" name="name" required>
<uni-easyinput trim="all" v-model="formData.name" placeholder="请输入姓名" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="手机号码" name="name" required>
<uni-easyinput trim="all" v-model="formData.name" placeholder="请输入手机号码" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="邮箱地址" name="name">
<uni-easyinput trim="all" v-model="formData.name" placeholder="请输入邮箱地址" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="性别" required>
<uni-data-checkbox v-model="formData.sex" :localdata="sexs" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="出生日期" name="name">
<uni-datetime-picker type="date" return-type="timestamp" v-model="formData.datetimesingle" />
</uni-forms-item>
</view>
</view>
<view class="smart-form-group">
<view class="smart-form-group-title"> 兴趣爱好 </view>
<view class="smart-form-group-content">
<uni-forms-item class="smart-form-item" label="兴趣爱好" name="interest">
<uni-data-checkbox mode="button" multiple v-model="formData.interest" :localdata="interestList"></uni-data-checkbox>
</uni-forms-item>
</view>
</view>
<view class="smart-form-group">
<view class="smart-form-group-title"> 屏幕设置 </view>
<view class="smart-form-group-content">
<uni-forms-item class="smart-form-item" label="亮度调整" name="name">
<slider style="width: 100%" value="50" activeColor="#2291F9" backgroundColor="#f5f6f8" block-color="#2291F9" block-size="20" />
</uni-forms-item>
</view>
</view>
<view class="smart-form-group">
<view class="smart-form-group-title"> 自我介绍 </view>
<view class="smart-form-group-content">
<uni-forms-item class="smart-form-item" label="兴趣爱好" name="interest">
<uni-easyinput type="textarea" autoHeight v-model="value" placeholder="请输入自我介绍" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="上传图片" name="interest">
<uni-file-picker limit="9" title="最多选择9张图片" />
</uni-forms-item>
</view>
</view>
</uni-forms>
<view class="smart-form-submit fixed-bottom-button">
<button class="smart-form-submit-btn smart-margin-right20" type="default">取消</button>
<button class="smart-form-submit-btn" type="warn">重置</button>
<button class="smart-form-submit-btn" type="primary">确定</button>
</view>
</view>
</template>
<script setup>
import { reactive } from 'vue';
const sexs = [
{
text: '男',
value: 0,
},
{
text: '女',
value: 1,
},
{
text: '你懂的',
value: 2,
},
];
const interestList = [
{ text: '唱歌', value: 1 },
{ text: '足球', value: 2 },
{ text: '篮球', value: 3 },
{ text: '跑步', value: 4 },
{ text: '写字', value: 5 },
{ text: '美术', value: 6 },
{ text: '射击', value: 7 },
{ text: '健身', value: 8 },
{ text: '马术', value: 9 },
{ text: '美食', value: 10 },
];
const formData = reactive({});
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,68 @@
<template>
<view class="list-container">
<view class="list-item" @click="gotoDetail(item.goodsId)" v-for="item in list" :key="item.noticeId">
<view class="title"> {{ item.goodsName }} </view>
<view class="author" v-if="item.remark"> 备注{{ item.remark }} </view>
<view class="author" v-if="item.categoryName"> 分类{{ item.categoryName }} </view>
<view class="author"> 创建时间{{ item.createTime }} </view>
</view>
</view>
</template>
<script setup>
const props = defineProps({
marginTop: {
type: String,
default: '0',
},
list: {
type: Array,
default: [],
},
});
function gotoDetail(goodsId) {
uni.navigateTo({ url: '/pages/goods/goods-detail?noticeId=' + goodsId });
}
</script>
<style lang="scss" scoped>
.list-container {
margin-top: v-bind(marginTop);
padding: 20rpx 10rpx;
}
.list-item {
box-sizing: border-box;
margin: 0 30rpx;
padding: 30rpx 0;
border-bottom: #cdcdcd 1px solid;
.title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
font-size: 16px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.author {
margin-bottom: 14rpx;
color: #999999;
font-size: 12px;
text-overflow: ellipsis;
}
.publish-time {
font-size: 12px;
color: #999999;
display: flex;
flex-direction: row;
justify-content: space-between;
.un-read {
color: $uni-color-primary;
}
}
}
</style>

View File

@ -0,0 +1,100 @@
<template>
<uni-popup ref="popupRef" background-color="#fff" type="bottom" :is-mask-click="false">
<view class="query-form-pop">
<view class="smart-form">
<uni-forms :label-width="100" :modelValue="form" label-position="left">
<view class="smart-form-group">
<view class="smart-form-group-title"> 产地 </view>
<view class="smart-form-group-content">
<DictSelect keyCode="GODOS_PLACE" v-model="form.place" @change="onPlaceChange" />
</view>
</view>
<view class="smart-form-group">
<view class="smart-form-group-title"> 状态 </view>
<view class="smart-form-group-content">
<SmartEnumSelect enumName="GOODS_STATUS_ENUM" @change="onGoodsStatusChange" v-model="form.goodsStatus" />
</view>
</view>
</uni-forms>
<view class="smart-form-submit smart-margin-top20">
<button class="smart-form-submit-btn smart-margin-right20" type="default" @click="cancel">取消</button>
<button class="smart-form-submit-btn" type="warn" @click="reset">重置</button>
<button class="smart-form-submit-btn" type="primary" @click="ok">确定</button>
</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { reactive, ref, toRaw } from 'vue';
import DictSelect from '/@/components/dict-select/index.vue';
import SmartEnumSelect from '/@/components/smart-enum-select/index.vue';
const emits = defineEmits(['close']);
defineExpose({ show });
// --------------------- ---------------------
const popupRef = ref();
function show() {
popupRef.value.open();
}
function close() {
popupRef.value.close();
}
// --------------------- ---------------------
function onPlaceChange(value, text) {
form.placeName = text;
}
function onGoodsStatusChange(value, text) {
form.goodsStatusName = text;
}
// --------------------- ---------------------
const defaultFormData = {
//
goodsStatus: undefined,
goodsStatusName: undefined,
//
place: undefined,
placeName: undefined,
};
const form = reactive({ ...defaultFormData });
// ----------------------- ------------------------
//
function cancel() {
close();
emits('close', null);
}
//
function reset() {
Object.assign(form, defaultFormData);
close();
emits('close', toRaw(form));
}
//
function ok() {
close();
emits('close', toRaw(form));
}
</script>
<style lang="scss" scoped>
.query-form-pop {
height: 800rpx;
overflow-y: scroll;
}
</style>

View File

@ -0,0 +1,222 @@
<template>
<view>
<mescroll-body @init="mescrollInit" :down="{ auto: false }" @down="onDown" @up="onUp">
<!--搜索框-->
<uni-nav-bar :border="false" fixed :leftWidth="0" rightWidth="70px">
<view class="input">
<uni-easyinput
prefixIcon="search"
:clearable="true"
trim="all"
v-model="queryForm.searchWord"
placeholder="搜索:商品名称"
@confirm="search"
@clear="search"
/>
</view>
<template #right>
<view class="nav-right" @click="showQueryFormPopUp">
<uni-icons type="settings" size="30"></uni-icons>
<view class="nav-right-name"> 筛选 </view>
</view>
</template>
</uni-nav-bar>
<!--筛选条件提示-->
<uni-notice-bar
@close="onCloseQueryFormTips"
v-if="showQueryFormTipsFlag"
class="query-bar"
background-color="#007aff"
color="white"
show-close
single
:text="queryFormTips"
/>
<!-- 筛选条件表单弹窗 -->
<QueryFormPopUp ref="queryFormPopUpRef" @close="onCloseQueryFormPopUp" />
<!-- 列表 -->
<NoticeList :list="listData" :margin-top="queryFormTipsMarginTop" />
</mescroll-body>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue';
import QueryFormPopUp from './components/goods-query-form-popup.vue';
import { goodsApi } from '/@/api/business/goods/goods-api';
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app';
import useMescroll from '@/uni_modules/uni-mescroll/hooks/useMescroll';
import { smartSentry } from '/@/lib/smart-sentry';
import NoticeList from './components/goods-list.vue';
import _ from 'lodash';
// --------------------------- ---------------------------------
const queryFormPopUpRef = ref();
/**
* 显示 筛选弹窗
*/
function showQueryFormPopUp() {
queryFormPopUpRef.value.show();
}
/**
* 监听 筛选弹窗 关闭
*/
function onCloseQueryFormPopUp(param) {
if (param === null) {
return;
}
Object.assign(queryForm, param);
showOrHideQueryFormTips();
query(getMescroll(), true, buildQueryParam(1));
uni.pageScrollTo({
scrollTop: 0,
});
}
// --------------------------- tips ---------------------------------
const queryFormTips = ref(null);
const showQueryFormTipsFlag = ref(false);
const queryFormTipsMarginTop = ref('0px');
/**
* 查询提示
*/
function buildQueryFormTips() {
let tips = null;
if (queryForm.searchWord) {
tips = '搜索:' + queryForm.searchWord;
}
if (queryForm.goodsStatusName) {
tips = tips ? tips + '' : '';
tips = tips + '商品状态:' + queryForm.goodsStatusName;
}
if (queryForm.placeName) {
tips = tips ? tips + '' : '';
tips = tips + '产地:' + queryForm.placeName;
}
return tips;
}
/**
* 显示或者隐藏tips
*/
function showOrHideQueryFormTips() {
let tips = buildQueryFormTips();
queryFormTipsMarginTop.value = _.isEmpty(tips) ? '0px' : '50rpx';
showQueryFormTipsFlag.value = !_.isEmpty(tips);
queryFormTips.value = tips;
}
/**
* 关闭筛选条件 tips清空搜索条件
*/
function onCloseQueryFormTips() {
Object.assign(queryForm, defaultForm);
queryFormTipsMarginTop.value = '0px';
showQueryFormTipsFlag.value = false;
queryFormTips.value = '';
search();
}
// --------------------------- ---------------------------------
const defaultForm = {
searchWord: '',
//
goodsStatus: undefined,
goodsStatusName: undefined,
//
place: undefined,
placeName: undefined,
pageNum: 1,
pageSize: 10,
};
//
const queryForm = reactive({ ...defaultForm });
//
const listData = ref([]);
function buildQueryParam(pageNum) {
queryForm.pageNum = pageNum;
return Object.assign({}, queryForm, { pageNum });
}
async function query(mescroll, isDownFlag, param) {
try {
let res = await goodsApi.queryGoodsList(param);
if (!isDownFlag) {
listData.value = listData.value.concat(res.data.list);
} else {
listData.value = res.data.list;
}
mescroll.endSuccess(res.data.list.length, res.data.pages > res.data.pageNum);
} catch (e) {
smartSentry.captureError(e);
//,
mescroll.endErr();
}
}
const { mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
/**
* 搜索
*/
function search() {
showOrHideQueryFormTips();
query(getMescroll(), true, buildQueryParam(1));
uni.pageScrollTo({
scrollTop: 0,
});
}
/**
* 下拉刷新
*/
function onDown(mescroll) {
queryForm.pageNum = 1;
query(mescroll, true, buildQueryParam(1));
}
/**
* 上拉加载更多
*/
function onUp(mescroll) {
query(mescroll, false, buildQueryParam(mescroll.num));
}
</script>
<style lang="scss" scoped>
.input {
width: 100%;
height: 72rpx;
background: #f7f8f9;
border-radius: 4px;
margin: 8rpx 0;
display: flex;
align-items: center;
}
.nav-right {
width: 140px;
display: flex;
height: 88rpx;
flex-direction: row;
line-height: 88rpx;
.nav-right-name {
margin-left: 5px;
line-height: 88rpx;
font-size: 30rpx;
}
}
.query-bar {
position: fixed;
}
</style>

View File

@ -0,0 +1,44 @@
<template>
<view class="banner" v-if="!$lodash.isEmpty(bannerList)">
<swiper class="swiper" circular :indicator-dots="true" :autoplay="true" indicator-active-color="#fff">
<swiper-item v-for="(item, index) in bannerList" :key="index">
<image class="swiper-item" :src="item.image" mode=""></image>
</swiper-item>
</swiper>
</view>
</template>
<script setup>
import { ref } from 'vue';
const bannerList = ref([
{
image: '/static/images/index/banner.png',
},
{
image: '/static/images/index/banner.png',
},
{
image: '/static/images/index/banner.png',
},
{
image: '/static/images/index/banner.png',
},
]);
</script>
<style lang="scss" scoped>
.banner {
border-radius: 16rpx;
overflow: hidden;
width: 700rpx;
height: 300rpx;
margin: 10rpx auto 20rpx;
.swiper {
width: 100%;
.swiper-item {
width: 100%;
height: 100%;
}
}
}
</style>

View File

@ -0,0 +1,135 @@
<template>
<view class="container">
<uni-card title="品质商品" :isFull="true" padding="0px" spacing="0px">
<view class="course">
<scroll-view class="scroll-view" scroll-x="true">
<view class="scroll-view-item" v-for="item in goodsList" :key="item.goodsId">
<div style="display: flex">
<image class="scroll-item-left" src="/static/logo.png" mode=""></image>
<view class="scroll-item-right">
<view class="item-title"> {{ item.categoryName }}-{{ item.goodsName }} </view>
<view class="item-right-bottom">
<view class="item-time"> 产地{{ item.place[0].valueName }}</view>
</view>
<view class="item-right-bottom">
<view class="item-time"> {{ item.remark }}</view>
<view class="item-reserved"> 预约 </view>
</view>
</view>
</div>
</view>
</scroll-view>
</view>
</uni-card>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { smartSentry } from '/@/lib/smart-sentry';
import { onShow } from '@dcloudio/uni-app';
import { goodsApi } from '/@/api/business/goods/goods-api';
const queryForm = {
pageNum: 1,
pageSize: 5,
searchCount: false,
};
const goodsList = ref([]);
async function queryGoodsList() {
try {
const result = await goodsApi.queryGoodsList(queryForm);
goodsList.value = result.data.list;
} catch (err) {
smartSentry.captureError(err);
}
}
onShow(() => {
queryGoodsList();
});
//
function onMore() {
router.push({
path: '/oa/notice/notice-employee-list',
});
}
function toDetail(noticeId) {
router.push({
path: '/oa/notice/notice-employee-detail',
query: { noticeId },
});
}
</script>
<style lang="scss" scoped>
.container {
width: 700rpx;
margin: 0 auto 20rpx;
border-radius: 12rpx;
overflow: hidden;
:deep(.uni-card__header-box) {
font-weight: bold;
}
:deep(.uni-card__content) {
padding: 0 !important;
}
:deep(.uni-card__header) {
background: linear-gradient(180deg, #e8f4ff, #f8fcff);
}
}
.course {
padding: 30rpx 0 30rpx 30rpx;
background-color: #fff;
}
.scroll-view-item {
display: inline-block;
width: 570rpx;
height: 128rpx;
overflow: hidden;
font-size: 36rpx;
border-right: 1rpx solid #ededed;
margin-right: 30rpx;
.scroll-item-left {
width: 128rpx;
height: 128rpx;
margin-right: 20rpx;
}
.scroll-item-right {
.item-title {
white-space: pre-wrap;
font-size: 30rpx;
width: 390rpx;
}
.item-right-bottom {
display: flex;
justify-content: space-between;
.item-time {
font-size: 12px;
font-weight: 400;
color: #777777;
letter-spacing: -0.02px;
}
.item-reserved {
width: 100rpx;
height: 40rpx;
background: #1a9aff;
border-radius: 4px;
box-shadow: 0 5rpx 8rpx 0 rgba(58, 121, 255, 0.2);
color: #fff;
text-align: center;
font-size: 26rpx;
line-height: 40rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,131 @@
<template>
<view class="menu-container">
<uni-grid :column="5" :highlight="true" :show-border="false" customStyle="display: block;">
<!--------------------------------- 第一排--------------------------------->
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="changeHome">
<image class="item-image" src="/@/static/images/index/ic_home_menu1.png"></image>
<view class="item-text"> 首页切换 </view>
</view>
</uni-grid-item>
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="navigateTo('/pages/notice/notice-index')">
<image class="item-image" src="/@/static/images/index/ic_home_menu2.png"></image>
<view class="item-text"> 通知公告 </view>
</view>
</uni-grid-item>
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="navigateTo('/pages/enterprise/enterprise-list')">
<image class="item-image" src="/@/static/images/index/ic_home_menu3.png"></image>
<view class="item-text"> 客户线索 </view>
</view>
</uni-grid-item>
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="navigateTo('/pages/goods/goods-index')">
<image class="item-image" src="/@/static/images/index/ic_home_menu4.png"></image>
<view class="item-text"> 品质商品 </view>
</view>
</uni-grid-item>
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="navigateTo('/pages/support/change-log/change-log-list')">
<image class="item-image" src="/@/static/images/index/ic_home_menu5.png"></image>
<view class="item-text"> 版本更新 </view>
</view>
</uni-grid-item>
<!--------------------------------- 第二排--------------------------------->
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="navigateTo('/pages/form/form')">
<image class="item-image" src="/@/static/images/index/ic_home_menu6.png"></image>
<view class="item-text"> 复杂表单 </view>
</view>
</uni-grid-item>
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="navigateTo('/pages/order-detail/order-detail')">
<image class="item-image" src="/@/static/images/index/ic_home_menu7.png"></image>
<view class="item-text"> 复杂详情 </view>
</view>
</uni-grid-item>
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="switchTab('/pages/list/list')">
<image class="item-image" src="/@/static/images/index/ic_home_menu9.png"></image>
<view class="item-text"> 列表样式1 </view>
</view>
</uni-grid-item>
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="switchTab('/pages/list2/list')">
<image class="item-image" src="/@/static/images/index/ic_home_menu8.png"></image>
<view class="item-text"> 列表样式2 </view>
</view>
</uni-grid-item>
<uni-grid-item class="menu-grid">
<view class="menu-item" @click="developing">
<image class="item-image" src="/@/static/images/index/ic_home_menu10.png"></image>
<view class="item-text"> 接口加密 </view>
</view>
</uni-grid-item>
</uni-grid>
</view>
</template>
<script setup>
import { SmartToast } from '/@/lib/smart-support';
const emit = defineEmits(['changeHome']);
function changeHome() {
emit('changeHome');
}
function navigateTo(url) {
uni.navigateTo({
url,
});
}
function switchTab(url) {
uni.switchTab({
url,
});
}
function developing() {
SmartToast.toast('敬请期待');
}
</script>
<style lang="scss" scoped>
.menu-container {
.menu-grid {
&:nth-child(n + 6) {
margin-top: 20rpx;
}
}
//height: 340rpx;
box-sizing: border-box;
border-radius: 16rpx;
width: 700rpx;
margin: 0 auto 20rpx;
display: flex;
justify-content: space-between;
padding: 30rpx 10rpx;
flex-wrap: wrap;
background-color: #fff;
.menu-item {
display: flex;
flex-direction: column;
align-items: center;
width: 134rpx;
height: 134rpx;
.item-image {
width: 80rpx;
height: 80rpx;
margin-bottom: 10rpx;
}
.item-text {
font-size: 26rpx;
color: #353535;
}
}
}
</style>

View File

@ -0,0 +1,96 @@
<template>
<view class="container">
<uni-card title="通知公告" :isFull="true" type="line" padding="0px" spacing="0px">
<template #extra> <view @click="onMore">查看更多</view> </template>
<uni-list>
<uni-list-item :ellipsis="1" v-for="item in data" :rightText="item.publishDate" clickable @click="goto(item.noticeId)" :title="item.title" />
</uni-list>
</uni-card>
</view>
</template>
<script setup>
import { ref } from 'vue';
import { noticeApi } from '/@/api/business/oa/notice-api';
import { smartSentry } from '/@/lib/smart-sentry';
import { onShow } from '@dcloudio/uni-app';
const queryForm = {
pageNum: 1,
pageSize: 5,
searchCount: false,
};
let data = ref([]);
const loading = ref(false);
//
async function queryNoticeList() {
try {
loading.value = true;
const result = await noticeApi.queryEmployeeNotice(queryForm);
data.value = result.data.list;
} catch (err) {
smartSentry.captureError(err);
} finally {
loading.value = false;
}
}
//
function goto(noticeId) {
uni.navigateTo({ url: '/pages/notice/notice-detail?noticeId=' + noticeId });
}
onShow(() => {
queryNoticeList();
});
//
function onMore() {
uni.navigateTo({
url: '/pages/notice/notice-index',
});
}
</script>
<style lang="scss" scoped>
.container {
width: 700rpx;
margin: 0 auto 20rpx;
border-radius: 12rpx;
padding: 0;
overflow: hidden;
:deep(.uni-card__header-box) {
font-weight: bold;
}
:deep(.uni-list--border-bottom) {
background-color: transparent;
}
:deep(.uni-card__content) {
padding: 0 !important;
}
:deep(.uni-card__header) {
border: none;
}
:deep(.uni-card .uni-card__header .uni-card__header-content .uni-card__header-content-title) {
font-size: 32rpx;
}
:deep(.uni-list-item__container) {
padding: 16rpx 20rpx;
}
:deep(.uni-card__header) {
background: linear-gradient(180deg, #e8f4ff, #f8fcff);
}
:deep(.uni-card__header-extra) {
font-size: 30rpx;
font-weight: 400;
text-align: center;
color: #1a9aff;
}
:deep(.uni-list-item__content-title) {
font-size: 30rpx;
}
}
</style>

View File

@ -0,0 +1,87 @@
<template>
<view class="statistics-view">
<view class="view-top">
<view class="view-top-item-left">
<view class="item-left-top"> 本月销售额 </view>
<view class="item-left-bottom"> ¥32,226.00 </view>
</view>
<view class="view-top-item-right">
<view class="item-right-top"> 本月销售目标 </view>
<view class="item-right-bottom"> ¥50,000.00 </view>
</view>
</view>
<view class="view-bottom">
<view class="view-bottom-item">
<view class="item-bottom-top"> 本月完成率 </view>
<view class="item-bottom-bottom"> 85% </view>
</view>
<view class="view-item">
<view class="item-bottom-top"> 本月新增客户 </view>
<view class="item-bottom-bottom">+1024 </view>
</view>
<view class="view-item">
<view class="item-bottom-top"> 本月累计通话 </view>
<view class="item-bottom-bottom"> 16:00小时 </view>
</view>
</view>
</view>
</template>
<script></script>
<style lang="scss" scoped>
.statistics-view {
width: 700rpx;
height: 300rpx;
margin: 10rpx auto 20rpx;
background: linear-gradient(133deg, #045ff0 0%, #2291f9 55%, #2168ec 100%);
border-radius: 6px;
.view-top {
display: flex;
padding: 36rpx 44rpx;
.view-top-item-left {
margin-right: 100rpx;
.item-left-top {
opacity: 0.6;
font-size: 26rpx;
color: #ffffff;
margin-bottom: 5rpx;
}
.item-left-bottom {
font-size: 52rpx;
font-weight: 700;
color: #ffffff;
}
}
.view-top-item-right {
.item-right-top {
opacity: 0.6;
font-size: 26rpx;
color: #ffffff;
margin-bottom: 20rpx;
}
.item-right-bottom {
font-size: 30rpx;
font-weight: 700;
color: #ffffff;
}
}
}
.view-bottom {
padding: 26rpx 44rpx;
display: flex;
justify-content: space-between;
.item-bottom-top {
opacity: 0.6;
font-size: 24rpx;
font-weight: 400;
color: #ffffff;
}
.item-bottom-bottom {
font-size: 30rpx;
font-weight: 500;
color: #ffffff;
}
}
}
</style>

View File

@ -0,0 +1,80 @@
<template>
<view class="page">
<uni-nav-bar title="首页" :border="false" fixed>
<template #right>
<view class="right">
<view class="">
<image src="/@/static/images/index/ic_scan.png" mode=""></image>
</view>
<view class="">
<image src="/@/static/images/index/ic_search.png" mode=""></image>
</view>
</view>
</template>
</uni-nav-bar>
<!-- 数据表 -->
<Statistics v-if="!showBannerFlag" />
<!-- Banner -->
<Banner v-if="showBannerFlag" />
<!-- 功能菜单 -->
<Menu @changeHome="changeHome" />
<!-- 通知公告 -->
<Notice />
<!-- 商品 -->
<Goods />
</view>
</template>
<script setup>
import Banner from './components/banner.vue';
import Menu from './components/menu.vue';
import Statistics from './components/statistics.vue';
import Notice from './components/notice.vue';
import Goods from './components/goods.vue';
import { ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
const showBannerFlag = ref(false);
function changeHome() {
showBannerFlag.value = !showBannerFlag.value;
}
onShow(() => {
uni.pageScrollTo({
scrollTop: 0,
duration: 300,
});
});
</script>
<style lang="scss" scoped>
.page {
background-color: #f5f5f5;
}
:deep(.uni-navbar__header-btns) {
width: 150rpx !important;
}
.right {
position: relative;
display: flex;
z-index: 999;
view {
width: 56rpx;
height: 56rpx;
margin-left: 36rpx;
image {
width: 100%;
height: 100%;
}
}
}
</style>

View File

@ -0,0 +1,154 @@
<template>
<view class="list-view">
<view class="item" v-for="item in data">
<view class="item-top">
<view class="item-top-info">
<view class="name"> {{ item.name }} </view>
<view class="mobile"> {{ item.phone }} </view>
</view>
<view class="item-top-phone"> <image src="/static/images/list/phone.png" mode=""></image> 联系Ta </view>
</view>
<view class="item-title"> 最后跟进内容{{ item.content }} </view>
<view class="item-bottom"> 最后跟进时间{{ item.time }} </view>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue';
const data = ref([
{
name: '卓大',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '胡可',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '开云',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '清野',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '善逸',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '飞叶',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '启阳',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '卓大',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '卓大',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '卓大',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '卓大',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
{
name: '卓大',
phone: '18610241024',
content: '上午跟客户谈了下合作意向,对方比较认可',
time: '2023-01-03 12:14:10',
},
]);
</script>
<style lang="scss" scoped>
.list-view {
margin: 20rpx 24rpx;
}
.item {
box-sizing: border-box;
width: 700rpx;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding: 24rpx 30rpx;
border-bottom: 1rpx solid #ededed;
margin-bottom: 24rpx;
.item-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.item-top-info {
display: flex;
align-items: center;
.name {
font-size: 34rpx;
font-weight: 500;
color: #444444;
margin-right: 10rpx;
}
.mobile {
font-size: 32rpx;
font-weight: bold;
text-align: left;
color: #444444;
}
}
.item-top-phone {
color: #1a9aff;
font-size: 28rpx;
display: flex;
align-items: center;
image {
width: 40rpx;
height: 40rpx;
margin-left: 4rpx;
}
}
}
.item-title {
font-size: 28rpx;
color: #777777;
margin-bottom: 24rpx;
}
.item-bottom {
font-size: 28rpx;
color: #777777;
}
}
</style>

View File

@ -0,0 +1,143 @@
<template>
<view class="list-view">
<view class="item">
<view class="item-header">
<view class="item-top">
<view class="item-top-left">订单号DD238647624875 </view>
<view class="item-top-right">
<view class="tag" type="warring"> 未付款 </view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-title"> 小米手机20S </view>
<view class="item-sub-title"> 订单金额¥5852.00 </view>
<view class="item-sub-title"> 客户1024创新实验室 </view>
<view class="item-sub-title"> 下单时间 2023-08-20 19:15 </view>
</view>
<view class="order-end-time"> 剩余 29:35:07 关闭订单 </view>
</view>
<view class="item">
<view class="item-header">
<view class="item-top">
<view class="item-top-left">订单号DD2382354875 </view>
<view class="item-top-right">
<view class="tag" type="success"> 完成 </view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-title"> 苹果Iphone30 </view>
<view class="item-sub-title"> 订单金额¥5852.00 </view>
<view class="item-sub-title"> 客户卓大 </view>
<view class="item-sub-title"> 下单时间 2023-08-20 19:15 </view>
</view>
</view>
<view class="item">
<view class="item-header">
<view class="item-top">
<view class="item-top-left">订单号DD238643287 </view>
<view class="item-top-right">
<view class="tag" type="success"> 完成 </view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-title"> 华为Mate100 </view>
<view class="item-sub-title"> 订单金额¥5852.00 </view>
<view class="item-sub-title"> 客户卓大 </view>
<view class="item-sub-title"> 下单时间 2023-08-20 19:15 </view>
</view>
</view>
</view>
</template>
<script setup>
defineProps({
list: {
type: Array,
default: [],
},
});
</script>
<style lang="scss" scoped>
.list-view {
margin: 20rpx 24rpx;
}
.item {
.item-header {
padding: 0 30rpx;
}
box-sizing: border-box;
width: 700rpx;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding-top: 24rpx;
margin-bottom: 30rpx;
overflow: hidden;
.item-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.item-top-left {
font-size: 28rpx;
font-weight: 400;
color: #777777;
}
.item-top-right {
.tag {
width: 112rpx;
height: 40rpx;
border-radius: 4rpx;
text-align: center;
line-height: 40rpx;
font-size: 24rpx;
&[type='warring'] {
color: #ff6c00;
background: #fff0ed;
}
&[type='success'] {
color: $uni-color-success;
background: #f7f8f9;
}
}
}
}
.item-title {
margin-top: 20rpx;
margin-bottom: 12rpx;
font-size: 34rpx;
font-weight: 700;
color: #444444;
}
.item-sub-title {
font-size: 28rpx;
color: #777777;
margin-top: 20rpx;
&:last-child {
padding-bottom: 20rpx;
margin-bottom: 0;
}
}
.order-end-time {
height: 40rpx;
background: #ff6c00;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
color: #fff;
text-align: center;
font-size: 24rpx;
line-height: 40rpx;
}
}
.line-border-bottom {
border-bottom: 1rpx solid #ededed;
}
</style>

View File

@ -0,0 +1,335 @@
<template>
<view class="list-view">
<view class="item">
<view class="item-header">
<view class="item-top">
<view class="item-top-left"> 2023-08-20 19:15 </view>
<view class="item-top-right">
<view class="tag" type="warring"> 未付款 </view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-center">
<view class="item-center-left">
<view class="item-top-z"> </view>
<view class="item-center"> </view>
<view class="item-bottom-x"> </view>
</view>
<view class="item-center-address">
<view class="m-b-16">
<view class="item-title"> 装货地址 </view>
<view class="item-address"> 河南省洛阳市洛龙区110号 </view>
</view>
<view class="m-b-16">
<view class="item-title"> 卸货地址 </view>
<view class="item-address"> 河南省洛阳市西工区120号 </view>
</view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-footer">
<view class="item-footer-left">
<text>586.00</text>
</view>
<view class="item-footer-right">
<view class="camera">
<image src="/static/images/list/camera.png" mode=""></image>
拍照
</view>
<view class="ok">
<image src="/static/images/list/success.png" mode=""></image>
完成
</view>
</view>
</view>
</view>
</view>
<view class="item">
<view class="item-header">
<view class="item-top">
<view class="item-top-left"> 2023-08-20 19:15 </view>
<view class="item-top-right">
<view class="tag" type="warring"> 未付款 </view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-center">
<view class="item-center-left">
<view class="item-top-z"> </view>
<view class="item-center"> </view>
<view class="item-bottom-x"> </view>
</view>
<view class="item-center-address">
<view class="m-b-16">
<view class="item-title"> 装货地址 </view>
<view class="item-address"> 河南省洛阳市洛龙区110号 </view>
</view>
<view class="m-b-16">
<view class="item-title"> 卸货地址 </view>
<view class="item-address"> 河南省洛阳市西工区120号 </view>
</view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-footer">
<view class="item-footer-left">
<text>586.00</text>
</view>
<view class="item-footer-right">
<view class="camera">
<image src="/static/images/list/camera.png" mode=""></image>
拍照
</view>
<view class="ok">
<image src="/static/images/list/success.png" mode=""></image>
完成
</view>
</view>
</view>
</view>
</view>
<view class="item">
<view class="item-header">
<view class="item-top">
<view class="item-top-left"> 2023-08-20 19:15 </view>
<view class="item-top-right">
<view class="tag" type="warring"> 未付款 </view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-center">
<view class="item-center-left">
<view class="item-top-z"> </view>
<view class="item-center"> </view>
<view class="item-bottom-x"> </view>
</view>
<view class="item-center-address">
<view class="m-b-16">
<view class="item-title"> 装货地址 </view>
<view class="item-address"> 河南省洛阳市洛龙区110号 </view>
</view>
<view class="m-b-16">
<view class="item-title"> 卸货地址 </view>
<view class="item-address"> 河南省洛阳市西工区120号 </view>
</view>
</view>
</view>
<view class="line-border-bottom"> </view>
<view class="item-footer">
<view class="item-footer-left">
<text>586.00</text>
</view>
<view class="item-footer-right">
<view class="camera">
<image src="/static/images/list/camera.png" mode=""></image>
拍照
</view>
<view class="ok">
<image src="/static/images/list/success.png" mode=""></image>
完成
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
defineProps({
list: {
type: Array,
default: [],
},
});
</script>
<style lang="scss" scoped>
.list-view {
margin: 20rpx 24rpx;
}
.item {
.item-header {
padding: 0 30rpx;
}
box-sizing: border-box;
width: 700rpx;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding-top: 24rpx;
margin-bottom: 30rpx;
overflow: hidden;
.item-top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
.item-top-left {
font-size: 28rpx;
font-weight: 400;
color: #777777;
}
.item-top-right {
.tag {
width: 112rpx;
height: 40rpx;
border-radius: 4rpx;
text-align: center;
line-height: 40rpx;
font-size: 24rpx;
&[type='warring'] {
color: #ff6c00;
background: #fff0ed;
}
&[type='success'] {
color: #1a9aff;
background: #f2f9ff;
}
}
}
}
.item-center {
margin-top: 20rpx;
display: flex;
.item-center-left {
margin-top: 5rpx;
margin-right: 12rpx;
display: flex;
flex-direction: column;
align-items: center;
.item-top-z {
position: relative;
z-index: 10;
width: 34rpx;
height: 34rpx;
background: #1a9aff;
border-radius: 12rpx;
text-align: center;
font-size: 24rpx;
color: #fff;
}
.item-center {
height: 75rpx;
border-left: 1rpx dashed #777777;
margin-top: -5rpx;
border-spacing: 6rpx;
border-width: 2rpx;
}
.item-bottom-x {
width: 34rpx;
height: 34rpx;
background: #ff6c00;
border-radius: 12rpx;
text-align: center;
font-size: 24rpx;
color: #fff;
}
}
.item-center-address {
.m-b-16 {
margin-bottom: 16rpx;
}
.item-title {
font-size: 34rpx;
color: #444444;
font-weight: bold;
margin-bottom: 4rpx;
}
.item-address {
font-size: 28rpx;
color: #777777;
}
}
}
.item-sub-title {
font-size: 28rpx;
color: #777777;
margin-bottom: 10rpx;
&:last-child {
padding-bottom: 20rpx;
margin-bottom: 0;
}
}
.order-end-time {
height: 40rpx;
background: #ff6c00;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
color: #fff;
text-align: center;
font-size: 24rpx;
line-height: 40rpx;
}
}
.item-footer {
padding: 20rpx 0;
display: flex;
justify-content: space-between;
align-items: center;
.item-footer-left {
font-size: 34rpx;
color: #777777;
text {
color: #ff3924;
font-weight: bold;
}
}
.item-footer-right {
display: flex;
align-items: center;
view {
width: 160rpx;
height: 64rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
image {
width: 40rpx;
height: 40rpx;
}
&.ok {
background: #1a9aff;
margin-left: 24rpx;
}
&.camera {
background: linear-gradient(107deg, #57d697 9%, #40c87e 86%);
}
}
}
}
.line-border-bottom {
border-bottom: 1rpx solid #ededed;
}
</style>

View File

@ -0,0 +1,246 @@
<template>
<view class="list-view">
<view class="item">
<view class="item-top">
<view class="item-top-item">
<view class="item-top-label"> 创建时间 </view>
<view class="item-title"> 2023-08-27 21:33 </view>
<view class="item-state">
<view class="tag" type="warring"> 未付款 </view>
</view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 客户名称 </view>
<view class="item-title"> 六边形工程师 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 供应商名称 </view>
<view class="item-title"> 1024数字科技有限公司 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 客户经理 </view>
<view class="item-title"> 卓大 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 生产厂商 </view>
<view class="item-title"> 1024工厂 </view>
</view>
</view>
<view class="item-footer">
<view class="item-footer-left"> 付款金额 </view>
<view class="item-footer-right"> 300000.00 </view>
</view>
</view>
<view class="item">
<view class="item-top">
<view class="item-top-item">
<view class="item-top-label"> 创建时间 </view>
<view class="item-title"> 2023-08-27 21:33 </view>
<view class="item-state">
<view class="tag" type="success"> 已付款 </view>
</view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 客户名称 </view>
<view class="item-title"> 六边形工程师 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 供应商名称 </view>
<view class="item-title"> 1024数字科技有限公司 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 客户经理 </view>
<view class="item-title"> 卓大 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 生产厂商 </view>
<view class="item-title"> 1024工厂 </view>
</view>
</view>
<view class="item-footer">
<view class="item-footer-left"> 付款金额 </view>
<view class="item-footer-right"> 300000.00 </view>
</view>
</view>
<view class="item">
<view class="item-top">
<view class="item-top-item">
<view class="item-top-label"> 创建时间 </view>
<view class="item-title"> 2024-01-11 21:33 </view>
<view class="item-state">
<view class="tag" type="warring"> 未付款 </view>
</view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 客户名称 </view>
<view class="item-title"> 孙传芳 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 供应商名称 </view>
<view class="item-title"> 1024数字科技有限公司 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 客户经理 </view>
<view class="item-title"> 胡克 </view>
</view>
<view class="item-top-item">
<view class="item-top-label"> 生产厂商 </view>
<view class="item-title"> 数字工厂 </view>
</view>
</view>
<view class="item-footer">
<view class="item-footer-left"> 付款金额 </view>
<view class="item-footer-right"> 300000.00 </view>
</view>
</view>
</view>
</template>
<script setup>
defineProps({
list: {
type: Array,
default: [],
},
});
</script>
<style lang="scss" scoped>
.decollator {
height: 30rpx;
position: relative;
display: flex;
align-items: center;
.decollator-left {
top: 2rpx;
position: absolute;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background: #f5f5f5;
z-index: 10;
left: -30rpx;
}
.decollator-right {
top: 2rpx;
width: 60rpx;
height: 60rpx;
border-radius: 50%;
background: #f5f5f5;
position: absolute;
z-index: 10;
right: -30rpx;
}
.decollator-center {
width: 100%;
position: absolute;
bottom: 0;
z-index: 1;
border-top: 1rpx dashed #ededed;
}
}
.list-view {
margin: 20rpx 24rpx;
}
.item {
box-sizing: border-box;
width: 700rpx;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding-top: 24rpx;
margin-bottom: 30rpx;
overflow: hidden;
.item-top {
padding: 0 30rpx;
.item-top-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
}
margin-bottom: 20rpx;
.item-top-label {
width: 168rpx;
margin-right: 10rpx;
font-size: 28rpx;
color: #777777;
}
.item-title {
font-size: 28rpx;
color: #323333;
font-weight: bold;
}
.item-state {
flex: 1;
display: flex;
justify-content: flex-end;
.tag {
width: 112rpx;
height: 40rpx;
border-radius: 4rpx;
text-align: center;
line-height: 40rpx;
font-size: 24rpx;
&[type='warring'] {
background-color: $error-color;
color: white;
}
&[type='success'] {
background-color: $uni-color-success;
color: white;
}
}
}
}
.item-sub-title {
font-size: 28rpx;
color: #777777;
margin-bottom: 10rpx;
&:last-child {
padding-bottom: 20rpx;
margin-bottom: 0;
}
}
.order-end-time {
height: 40rpx;
background: #ff6c00;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
color: #fff;
text-align: center;
font-size: 24rpx;
line-height: 40rpx;
}
}
.line-border-bottom {
border-bottom: 1rpx solid #ededed;
}
.item-footer {
height: 120rpx;
display: flex;
border-top: 1rpx dashed #ccc;
//background-color: #F7FAFD;
align-items: center;
justify-content: space-between;
margin: 0 30rpx;
.item-footer-left {
font-size: 28rpx;
color: #777;
}
.item-footer-right {
font-size: 34rpx;
color: red;
font-weight: bold;
}
}
</style>

View File

@ -0,0 +1,47 @@
<template>
<view>
<y-tabs v-model="active" sticky :offsetTop="43" color="#007aff">
<y-tab v-for="item in tabsList" :key="item.value" :title="item.title"> </y-tab>
</y-tabs>
<ListUI1 v-if="active === 0" />
<ListUI2 v-if="active === 1" />
<ListUI3 v-if="active === 2" />
<ListUI4 v-if="active === 3" />
</view>
</template>
<script setup>
import { ref } from 'vue';
import ListUI1 from './components/list-ui1.vue';
import ListUI2 from './components/list-ui2.vue';
import ListUI3 from './components/list-ui3.vue';
import ListUI4 from './components/list-ui4.vue';
const active = ref(0);
const tabsList = [
{
title: '线索列表',
value: 0,
},
{
title: '我的订单',
value: 1,
},
{
title: '我的运单',
value: 2,
},
{
title: '付款单',
value: 3,
},
];
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
</style>

View File

@ -0,0 +1,108 @@
<template>
<view class="item" v-for="item in 10">
<view class="item-header">
<view class="header-title"> 商业环境快速变化带来的挑战 </view>
<view class="header-right"> 共15讲> </view>
</view>
<view class="item-tags">
<view class="primary"> 房地产 </view>
<view class="success"> 国债 </view>
<view class="warning"> 贸易 </view>
</view>
<view class="content">
<view class="content-box">
<view class="c-top"> 授课老师 </view>
<view class="c-bottom"> 秦始皇 </view>
</view>
<view class="content-box">
<view class="c-top"> 开课时间 </view>
<view class="c-bottom"> 2023.08.20 20:00 </view>
</view>
</view>
</view>
</template>
<script setup>
defineProps({
list: {
type: Array,
default: [],
},
});
</script>
<style lang="scss" scoped>
.item {
width: 700rpx;
margin: 20rpx auto 0;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding: 26rpx 30rpx 20rpx;
box-sizing: border-box;
.item-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
.header-title {
width: 480rpx;
font-size: 34rpx;
color: #444;
font-weight: 600;
}
.header-right {
margin-top: 4rpx;
font-size: 28rpx;
color: #777777;
}
}
.item-tags {
display: flex;
margin: 10rpx 0;
view {
width: 112rpx;
height: 42rpx;
border-radius: 4rpx;
text-align: center;
line-height: 42rpx;
font-size: 24rpx;
margin-right: 24rpx;
&.primary {
background: #eef7fd;
color: #1a9aff;
}
&.success {
color: #1ce36d;
background: #e5f8e9;
}
&.warning {
background: #fff0ed;
color: #ff6c00;
}
}
}
.content {
width: 100%;
height: 140rpx;
background: #f7f8f9;
border-radius: 4px;
display: flex;
justify-content: space-around;
align-items: center;
.content-box {
display: flex;
flex-direction: column;
align-items: center;
}
.c-top {
font-size: 24rpx;
color: #777777;
margin-bottom: 4rpx;
}
.c-bottom {
font-size: 30rpx;
color: #444444;
}
}
}
</style>

View File

@ -0,0 +1,149 @@
<template>
<view class="item">
<view class="header">
<view class="header-left">
<view class="header-left-title"> 手机优惠券 </view>
<view class="header-left-time"> 有效期至2024.08.28 </view>
</view>
<view class="header-right-price"> <text>10</text> </view>
</view>
<view class="footer">
<view class="footer-left"> 适用于双11商品 </view>
<view v-if="index != 1" class="footer-right"> 去使用 </view>
</view>
</view>
<view class="item">
<view class="forbidden">
<image src="/static/images/pure-list/employ.png" mode=""></image>
</view>
<view class="header">
<view class="header-left">
<view class="header-left-title"> 笔记本优惠券 </view>
<view class="header-left-time"> 有效期至2024.08.28 </view>
</view>
<view class="header-right-price grayscale"> <text>99</text> </view>
</view>
<view class="footer">
<view class="footer-left"> 适用于双11商品 </view>
</view>
</view>
<view class="item">
<view class="header">
<view class="header-left">
<view class="header-left-title"> 汽车优惠券 </view>
<view class="header-left-time"> 有效期至2024.08.28 </view>
</view>
<view class="header-right-price"> <text>9999</text> </view>
</view>
<view class="footer">
<view class="footer-left"> 适用于双11商品 </view>
<view v-if="index != 1" class="footer-right"> 去使用 </view>
</view>
</view>
<view class="item">
<view class="forbidden">
<image src="/static/images/pure-list/employ.png" mode=""></image>
</view>
<view class="header">
<view class="header-left">
<view class="header-left-title"> 笔记本优惠券 </view>
<view class="header-left-time"> 有效期至2024.08.28 </view>
</view>
<view class="header-right-price grayscale"> <text>99</text> </view>
</view>
<view class="footer">
<view class="footer-left"> 适用于双11商品 </view>
</view>
</view>
</template>
<script setup>
defineProps({
list: {
type: Array,
default: [],
},
});
</script>
<style lang="scss" scoped>
.forbidden {
position: absolute;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.5);
left: 0;
top: 0;
image {
position: absolute;
right: 0;
bottom: 0;
width: 184rpx;
height: 160rpx;
}
}
.item {
position: relative;
width: 700rpx;
margin: 20rpx auto 0;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding: 26rpx 40rpx 20rpx;
box-sizing: border-box;
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 20rpx;
border-bottom: 1rpx dashed #ededed;
margin-bottom: 16rpx;
.header-left {
.header-left-title {
font-size: 34rpx;
font-weight: 500;
text-align: left;
color: #444444;
margin-bottom: 8rpx;
}
.header-left-time {
font-size: 28rpx;
color: #777;
}
}
.header-right-price {
color: #ff3924;
font-size: 42rpx;
font-weight: bold;
text {
font-size: 72rpx;
}
}
.grayscale {
color: #ccc;
}
}
.footer {
height: 64rpx;
display: flex;
justify-content: space-between;
align-items: center;
.footer-left {
font-size: 28rpx;
color: #777777;
}
.footer-right {
width: 144rpx;
height: 64rpx;
background: linear-gradient(112deg, #f7681d 9%, #f64226 84%);
border-radius: 8rpx;
box-shadow: 0px 5px 8px 0px rgba(236, 79, 40, 0.2);
color: #fff;
font-size: 28rpx;
text-align: center;
line-height: 64rpx;
}
}
}
</style>

View File

@ -0,0 +1,159 @@
<template>
<view class="item" v-for="(item, index) in 6">
<view class="item-header">
<view class="header-title"> 快递号KD12382387 </view>
<view class="header-right" :class="index == 1 ? 'pay' : 'nopay'">
{{ index == 1 ? '已支付' : '未支付' }}
</view>
</view>
<view class="address">
<view class="start-address">
<view class="atom start">
<view> </view>
</view>
<view class="start-address-right">
<view class="start-address-text"> 出发地洛阳市六边形科技园 </view>
<view class="start-time"> 出发时间2023.08.23 </view>
</view>
</view>
<view class="end-address">
<view class="atom end">
<view> </view>
</view>
<view class="start-address-text"> 目的地上海市壹零贰肆软件园 </view>
</view>
</view>
<view class="footer">
<view class="footer-order-time"> 下单时间2023.08.23 </view>
<view class="footer-order-price"> 订单金额<text class="pay" :class="index == 1 ? 'pay' : 'nopay'">¥450.00</text> </view>
</view>
</view>
</template>
<script setup>
defineProps({
list: {
type: Array,
default: [],
},
});
</script>
<style lang="scss" scoped>
.item {
width: 700rpx;
margin: 30rpx auto 0;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding: 26rpx 30rpx 20rpx;
box-sizing: border-box;
.item-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
position: relative;
margin-bottom: 26rpx;
.header-title {
width: 480rpx;
font-size: 28rpx;
font-weight: bold;
color: #444;
}
.header-right {
position: absolute;
right: -40rpx;
background-size: 138rpx 60rpx;
width: 138rpx;
height: 60rpx;
font-size: 28rpx;
color: #fff;
line-height: 50rpx;
text-indent: 24rpx;
&.pay {
background-image: url('/static/images/pure-list/blue.png');
}
&.nopay {
background-image: url('/static/images/pure-list/orange.png');
}
}
}
.address {
width: 100%;
background: #f7f8f9;
border-radius: 4px;
padding: 20rpx 24rpx;
box-sizing: border-box;
margin-bottom: 30rpx;
.atom {
width: 30rpx;
height: 30rpx;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
margin-right: 10rpx;
margin-top: 6rpx;
view {
width: 14rpx;
height: 14rpx;
border-radius: 50%;
}
&.start {
background: rgba(26, 154, 255, 0.2);
view {
background: rgb(26, 154, 255);
}
}
&.end {
background: rgba(255, 108, 0, 0.2);
view {
background: rgb(255, 108, 0);
}
}
}
.end-address {
display: flex;
align-items: flex-start;
}
.start-address-text {
font-size: 30rpx;
font-weight: bold;
color: #444444;
margin-bottom: 4rpx;
}
.start-address {
display: flex;
align-items: flex-start;
.start-time {
font-size: 28rpx;
color: #777777;
}
margin-bottom: 20rpx;
}
}
.footer {
display: flex;
justify-content: space-between;
.footer-order-time {
font-size: 28rpx;
color: #777;
}
.footer-order-price {
color: #777;
font-size: 28rpx;
text {
font-weight: bold;
margin-left: 4rpx;
&.pay {
color: #323333;
}
&.nopay {
color: #ff3924;
}
}
}
}
}
</style>

View File

@ -0,0 +1,94 @@
<template>
<view class="item" v-for="(item, index) in 12" :key="index" :class="type[index]">
<view class="header"> PLC控制柜 </view>
<view class="footer">
<view class="footer-left"> 气路总阀{{ date }} </view>
<view class="footer-state" :class="type[index]"> 关闭 </view>
</view>
</view>
</template>
<script setup>
import dayjs from 'dayjs';
defineProps({
list: {
type: Array,
default: [],
},
});
const date = dayjs().format('YYYY-MM-DD HH:mm:ss');
const type = [
'info',
'primary',
'warning',
'',
'info',
'primary',
'warning',
'',
'info',
'primary',
'warning',
'',
'info',
'primary',
'warning',
'',
];
</script>
<style lang="scss" scoped>
.item {
width: 700rpx;
margin: 20rpx auto 0;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding: 26rpx 40rpx 20rpx;
box-sizing: border-box;
border-left: 8rpx solid #4f4d4d;
&.primary {
border-color: #007ff2;
}
&.warning {
border-color: #ff6c00;
}
&.info {
border-color: #cccccc;
}
.header {
display: flex;
justify-content: space-between;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #ededed;
margin-bottom: 19rpx;
font-size: 34rpx;
color: #444;
}
.footer {
display: flex;
justify-content: space-between;
font-size: 28rpx;
.footer-left {
color: #777777;
}
.footer-state {
&.primary {
color: #007ff2;
}
&.warning {
color: #ff6c00;
}
&.info {
color: #cccccc;
}
color: #4f4d4d;
}
}
}
</style>

View File

@ -0,0 +1,100 @@
<template>
<view class="item" v-for="(item, index) in 6" :key="index">
<view class="header">
<view class="header-left">
<image class="header-left-image" src="/static/images/pure-list/maintain.png" mode=""></image>
<view class="header-left-title"> 反馈信息 </view>
</view>
<view class="header-right-id"> 保修单还12876 </view>
</view>
<view class="footer">
<view class="footer-item">
<view class="label"> 商品名称</view>
<view class="info"> 家用净水器 </view>
</view>
<view class="footer-item">
<view class="label"> 描述情况</view>
<view class="info"> 水有杂志且存在异味 </view>
</view>
<view class="footer-item">
<view class="label">提交人</view>
<view class="info"> 刘邦</view>
</view>
<view class="footer-item">
<view class="label"> 受理人员 </view>
<view class="info"> 迪丽热巴 </view>
</view>
<view class="footer-item">
<view class="label"> 提交时间 </view>
<view class="info"> 2023-01-01 12:12:12 </view>
</view>
</view>
</view>
</template>
<script setup>
defineProps({
list: {
type: Array,
default: [],
},
});
</script>
<style lang="scss" scoped>
.item {
position: relative;
width: 700rpx;
margin: 20rpx auto 0;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
padding: 26rpx 40rpx 20rpx;
box-sizing: border-box;
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #ededed;
margin-bottom: 16rpx;
.header-left {
display: flex;
align-items: center;
.header-left-image {
width: 48rpx;
height: 48rpx;
margin-right: 16rpx;
}
.header-left-title {
font-size: 34rpx;
font-weight: bold;
color: #444444;
}
}
.header-right-id {
font-size: 28rpx;
color: #777777;
}
}
.footer {
.footer-item {
display: flex;
align-items: center;
margin-bottom: 16rpx;
.label {
width: 80px;
font-size: 28rpx;
color: #777777;
}
.info {
font-size: 28rpx;
color: #323333;
font-weight: bold;
}
}
}
}
</style>

View File

@ -0,0 +1,55 @@
<template>
<view>
<y-tabs v-model="active" sticky :offsetTop="43" color="#007aff">
<y-tab v-for="item in tabsList" :key="item.value" :title="item.title"> </y-tab>
</y-tabs>
<ExpressList v-if="active === 0" />
<DiscountList v-if="active === 1" />
<IotList v-if="active === 2" />
<ServiceList v-if="active === 3" />
<CourseList v-if="active === 4" />
</view>
</template>
<script setup>
import CourseList from './components/course-list.vue';
import ExpressList from './components/express-list.vue';
import DiscountList from './components/discount-list.vue';
import ServiceList from './components/service-list.vue';
import IotList from './components/iot-list.vue';
import { ref } from 'vue';
const active = ref(0);
const tabsList = [
{
title: '快递',
value: 0,
},
{
title: '优惠卷',
value: 1,
},
{
title: '物联网',
value: 2,
},
{
title: '售后',
value: 3,
},
{
title: '课程',
value: 4,
},
];
</script>
<style lang="scss" scoped>
page {
background-color: #f5f5f5;
}
</style>

View File

@ -0,0 +1,57 @@
<template>
<view class="check-box">
<view class="check-item">
<image @click="agree" :src="!agreeFlag ? checkOutImg : checkInImg" />
<span>
我已阅读并同意
<span class="link" @click="openProtocol('user_agreement')">用户协议</span>
<span class="link" @click="openProtocol('privacy_terms')">隐私政策</span>
</span>
</view>
</view>
</template>
<script setup>
import checkOutImg from '/@/static/images/login/check-out.png';
import checkInImg from '/@/static/images/login/check-in.png';
import { ref } from 'vue';
const agreeFlag = ref(true);
function agree() {
agreeFlag.value = !agreeFlag.value;
}
function openProtocol(protocolKey) {
uni.navigateTo({
url: `/pages/protocol/index?key=${protocolKey}`,
});
}
defineExpose({
agreeFlag,
});
</script>
<style lang="scss" scoped>
.check-box {
.check-item {
display: flex;
align-items: center;
font-size: 12px;
font-weight: 400;
color: #999999;
margin-bottom: 20rpx;
image {
width: 24px;
height: 24px;
margin-right: 2px;
}
.link {
color: $main-color;
}
}
}
</style>

View File

@ -0,0 +1,139 @@
<template>
<view class="other-way-box">
<view class="title">
<span style="margin: 0 10px"> 第三方账号登录 </span>
</view>
<view class="other-way">
<!-- 手机号登录 -->
<view v-if="phoneLoginFlag" @click="navigateTo('/pages/login/phone-login')" class="item">
<image src="/@/static/images/login/phone-login-icon.png" />
</view>
<!-- 微信登录 -->
<view v-if="wxLoginFlag" @click="toWeChatLogin" class="item">
<image src="/@/static/images/login/wx-login-icon.png" />
</view>
<!-- 苹果账号登录 -->
<view v-if="iosFlag" @click="toAppleLogin" class="item apple">
<image src="/@/static/images/login/ios-login-icon.png" />
</view>
</view>
</view>
</template>
<script setup>
import { computed } from 'vue';
const emit = defineEmits(['wechatLogin', 'appleLogin']);
const phoneLoginFlag = computed(() => {
let currentPages = getCurrentPages();
let currentPage = currentPages[currentPages.length - 1];
return currentPage.route === 'pages/login/phone-login';
});
const wxLoginFlag = computed(() => {
let currentPages = getCurrentPages();
let currentPage = currentPages[currentPages.length - 1];
return currentPage.route === 'pages/login/login';
});
const iosFlag = computed(() => {
// #ifdef APP-PLUS
let systemInfoSync = uni.getSystemInfoSync();
let platform = systemInfoSync.platform;
return platform === 'ios';
// #endif
return true;
});
function navigateTo(url) {
uni.navigateTo({ url });
}
//
function toWeChatLogin() {
emit('wechatLogin');
}
//
function toAppleLogin() {
//
var appleOauth = null;
plus.oauth.getServices(
(services) => {
for (var i in services) {
var service = services[i];
// id 'apple' iOS13 service
if (service.id == 'apple') {
appleOauth = service;
break;
}
}
appleOauth.logout(
(success) => {
// console.log("退", JSON.stringify(success))
appleOauth.login(
(oauth) => {
// oauth.target.appleInfo
let info = oauth.target.appleInfo || {};
// console.log("", JSON.stringify(info))
emit('appleLogin', info);
},
(err) => {
// error
},
{
// scope: 'email'
scope: 'email',
}
);
},
(err) => {
console.log('退出登录失败');
}
);
},
(err) => {
// services
}
);
}
</script>
<style lang="scss" scoped>
.other-way-box {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.title {
font-size: $small-size;
font-weight: 400;
color: #cccccc;
}
.other-way {
margin-top: 42rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
.item {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
font-size: $main-size;
font-weight: 400;
color: #666666;
&.apple {
margin-left: 40px;
}
image {
width: 70rpx;
height: 70rpx;
}
}
}
}
</style>

View File

@ -0,0 +1,299 @@
<template>
<view class="container">
<view class="top-view">
<view class="login"> 登录 </view>
<view class="logo">
<image src="/@/static/images/login/login-logo.png" />
</view>
</view>
<view class="bottom-view">
<view class="input-view smart-margin-top10">
<image src="/@/static/images/login/login-username.png"></image>
<uni-easyinput
class="input"
placeholder="请输入用户名"
:clearable="true"
placeholderStyle="color:#CCCCCC"
border="none"
v-model="loginForm.loginName"
/>
</view>
<view class="input-view smart-margin-top10">
<image src="/@/static/images/login/login-password.png"></image>
<uni-easyinput
class="input"
placeholder="请输入密码"
:clearable="true"
:password="true"
placeholderStyle="color:#CCCCCC"
border="none"
v-model="loginForm.password"
/>
</view>
<view class="input-view smart-margin-top10">
<image src="/@/static/images/login/login-password.png"></image>
<uni-easyinput
class="input"
placeholder="请输入验证码"
:clearable="true"
:password="false"
placeholderStyle="color:#CCCCCC"
border="none"
v-model="loginForm.captchaCode"
style="width: 50%"
/>
<img class="captcha-img" :src="captchaBase64Image" @click="getCaptcha" />
</view>
<view class="code-login-view smart-margin-top10">
<text class="code-text">验证码登录</text>
<text class="forget-text">忘记密码</text>
</view>
<view @click="login" class="button login-btn smart-margin-top20"> 登录 </view>
<view @click="login" class="button register-btn smart-margin-top20"> 创建账号 </view>
<OtherWayBox class="other-way-box" />
<LoginCheckBox class="login-check-box" ref="loginCheckBoxRef" />
</view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue';
import { onShow } from '@dcloudio/uni-app';
import OtherWayBox from './components/other-way-box.vue';
import LoginCheckBox from './components/login-check-box.vue';
import { loginApi } from '/@/api/system/login-api';
import { LOGIN_DEVICE_ENUM } from '/@/constants/system/login-device-const';
import { encryptData } from '/@/lib/encrypt';
import { useUserStore } from '/@/store/modules/system/user';
import { smartSentry } from '/@/lib/smart-sentry';
const loginForm = reactive({
loginName: 'admin',
password: '123456',
captchaCode: '',
captchaUuid: '',
loginDevice: LOGIN_DEVICE_ENUM.H5.value,
});
const loginCheckBoxRef = ref();
async function login() {
if (!loginCheckBoxRef.value.agreeFlag) {
uni.showToast({
icon: 'none',
title: '请阅读并同意《用户协议》、《隐私政策》',
});
return;
}
if (!loginForm.loginName) {
uni.showToast({
icon: 'none',
title: '请输入用户名',
});
return;
}
if (!loginForm.password) {
uni.showToast({
icon: 'none',
title: '请输入密码',
});
return;
}
try {
uni.showLoading({ title: '登录中' });
//
let encryptPasswordForm = Object.assign({}, loginForm, {
password: encryptData(loginForm.password),
});
const res = await loginApi.login(encryptPasswordForm);
stopRefreshCaptchaInterval();
uni.showToast({ title: '登录成功' });
// pinia
useUserStore().setUserLoginInfo(res.data);
uni.switchTab({ url: '/pages/home/index' });
} catch (e) {
if (e.data && e.data.code !== 0) {
loginForm.captchaCode = '';
getCaptcha();
}
smartSentry.captureError(e);
uni.hideLoading();
}
}
//--------------------- ---------------------------------
const captchaBase64Image = ref('');
async function getCaptcha() {
try {
let captchaResult = await loginApi.getCaptcha();
captchaBase64Image.value = captchaResult.data.captchaBase64Image;
console.log(captchaResult.data.captchaBase64Image, 2);
loginForm.captchaUuid = captchaResult.data.captchaUuid;
beginRefreshCaptchaInterval(captchaResult.data.expireSeconds);
} catch (e) {
console.log(e);
}
}
let refreshCaptchaInterval = null;
function beginRefreshCaptchaInterval(expireSeconds) {
if (refreshCaptchaInterval === null) {
refreshCaptchaInterval = setInterval(getCaptcha, (expireSeconds - 5) * 1000);
}
}
function stopRefreshCaptchaInterval() {
if (refreshCaptchaInterval != null) {
clearInterval(refreshCaptchaInterval);
refreshCaptchaInterval = null;
}
}
onShow(getCaptcha);
</script>
<style lang="scss" scoped>
.bottom-view {
box-sizing: border-box;
margin-top: -280rpx;
border-radius: 20rpx 20rpx 0 0;
width: 100%;
background-color: white;
padding: 0 60rpx;
.input-view {
display: flex;
flex-direction: row;
align-items: center;
background-color: $page-bg-color;
border-radius: 4px;
height: 100rpx;
.captcha-img {
margin-left: 5px;
height: 100rpx;
}
image {
margin-left: 30rpx;
width: 44rpx;
height: 44rpx;
}
.input {
margin: 0 16rpx;
background-color: $page-bg-color;
}
}
.code-login-view {
margin: 50rpx 0 0;
height: 40rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
.code-text {
height: 40rpx;
font-size: $main-size;
font-weight: 400;
text-align: left;
color: $main-font-color;
}
.forget-text {
height: 40rpx;
font-size: $main-size;
font-weight: 400;
text-align: right;
color: $second-font-color;
}
}
}
.button {
flex-shrink: 0;
width: 100%;
height: 90rpx;
border-radius: 4px;
box-shadow: 0px 5px 8px 0px rgba(58, 121, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
font-size: $main-size;
&.disabled {
opacity: 0.4;
}
&.login-btn {
background: $main-color;
color: #ffffff;
}
&.register-btn {
background: white;
color: $main-color;
border: 0.5px solid $main-color;
border-color: rgba(26, 154, 255, 0.3);
}
}
.logo {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
height: 220rpx;
image {
width: 208rpx;
height: 220rpx;
}
}
::v-deep .uni-easyinput__content {
background-color: transparent !important;
}
::v-deep .is-input-border {
border: none;
}
.container {
display: flex;
align-items: center;
flex-direction: column;
min-height: 100vh;
width: 100vw;
.back-icon {
width: 18px;
height: 18px;
}
.top-view {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: 720rpx;
background-image: url(/@/static/images/login/login-top-back.png);
.login {
font-weight: bold;
margin-top: 70rpx;
}
.logo {
width: 260rpx;
height: 260rpx;
}
}
}
.other-way-box {
flex-shrink: 0;
margin-top: 82rpx;
}
.login-check-box {
flex-shrink: 0;
margin-top: 150rpx;
margin-bottom: 120rpx;
align-self: flex-start;
}
</style>

View File

@ -0,0 +1,88 @@
<template>
<view class="container">
<view class="message" v-for="(item, index) in 10">
<view class="message-header">
<view class="header-left">
<image src="/src/static/images/message/message.png" mode=""></image>
<view> 系统通知 </view>
</view>
<view class="header-time"> 2023-08-20 20:48:21 </view>
</view>
<view class="content">
<view class="message-title">
报销-付款已完成
<uni-icons v-if="index % 2 === 1" color="red" class="smart-margin-left10" type="info-filled" :size="14"></uni-icons>
</view>
<view class="message-body"> 您的费用保修单已完成付款金额300.00请注意查收 </view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {};
},
methods: {},
};
</script>
<style lang="scss" scoped>
.message {
width: 700rpx;
background: #ffffff;
border-radius: 12rpx;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
margin: 24rpx auto 0;
box-sizing: border-box;
padding: 30rpx 30rpx 24rpx;
.message-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24rpx;
.header-left {
align-items: center;
display: flex;
image {
width: 48rpx;
height: 48rpx;
margin-right: 14rpx;
}
font-size: 34rpx;
color: #000000;
}
.header-time {
font-size: 28rpx;
font-weight: 400;
color: #999999;
}
}
.content {
box-sizing: border-box;
padding: 24rpx;
background-color: #f7f8f9;
width: 100%;
border-radius: 8rpx;
.message-title {
color: #323333;
font-size: 34rpx;
font-weight: bold;
margin-bottom: 8rpx;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.message-body {
font-size: 28rpx;
color: #777777;
}
}
}
page {
background-color: #f5f5f5;
}
</style>

View File

@ -0,0 +1,108 @@
<template>
<view>
<view class="menu-list">
<uni-list>
<uni-list-item title="切换样式" link showBadge @click="changeStyle">
<template #header>
<image class="icon" src="/static/images/mine/mine-account.png" mode=""></image>
</template>
</uni-list-item>
<uni-list-item title="消息通知" link="switchTab" showBadge badgeText="6" badgeType="error" to="/pages/message/message">
<template #header>
<image class="icon" src="/static/images/mine/mine-message.png" mode=""></image>
</template>
</uni-list-item>
<uni-list-item title="意见反馈" link rightText="欢迎吐槽" showBadge to="/pages/support/feedback/feedback-form">
<template #header>
<image class="icon" src="/static/images/mine/mine-feedback.png" mode=""></image>
</template>
</uni-list-item>
<uni-list-item title="联系客服" showBadge clickable @click="callService">
<template #header>
<image class="icon" src="/static/images/mine/mine-service.png" mode=""></image>
</template>
<template #footer>
<view style="font-size: 30rpx; color: #1a9aff; line-height: 45rpx"> 18810241024 </view>
</template>
</uni-list-item>
<uni-list-item title="账号管理" showBadge link>
<template #header>
<image class="icon" src="/static/images/mine/mine-account.png" mode=""></image>
</template>
</uni-list-item>
</uni-list>
</view>
<view class="menu-list">
<uni-list>
<uni-list-item title="关于我们" link showBadge>
<template #header>
<image class="icon" src="/static/images/mine/mine-about-us.png" mode=""></image>
</template>
</uni-list-item>
<uni-list-item title="版本信息" link rightText="V1.0" showBadge>
<template #header>
<image class="icon" src="/static/images/mine/mine-version-info.png" mode=""></image>
</template>
</uni-list-item>
<uni-list-item title="隐私条款" link showBadge>
<template #header>
<image class="icon" src="/static/images/mine/user-agreement-icon.png" mode=""></image>
</template>
</uni-list-item>
<uni-list-item title="用户协议" link showBadge>
<template #header>
<image class="icon" src="/static/images/mine/mine-about-us.png" mode=""></image>
</template>
</uni-list-item>
<uni-list-item title="设置" showBadge clickable @click="developing">
<template #header>
<image class="icon" src="/static/images/mine/mine-protocol.png"></image>
</template>
</uni-list-item>
</uni-list>
</view>
</view>
</template>
<script setup>
import { SmartToast } from '/@/lib/smart-support';
const emits = defineEmits(['changeStyle']);
function changeStyle() {
emits('changeStyle');
}
function developing() {
SmartToast.toast('敬请期待');
}
function callService() {
uni.makePhoneCall({
phoneNumber: '18637925892',
});
}
</script>
<style scoped lang="scss">
.menu-list {
box-sizing: border-box;
background-color: #fff;
margin: 16px 12px 0 12px;
border-radius: 6px;
overflow: hidden;
box-shadow: 0 3px 4px 0 rgba(24, 144, 255, 0.06);
padding: 0 10rpx;
:deep(.uni-list-item__content-title) {
font-size: 30rpx;
}
:deep(.uni-list-item__extra-text) {
font-size: 28rpx;
}
.icon {
width: 56rpx;
height: 56rpx;
margin-right: 20rpx;
}
}
</style>

View File

@ -0,0 +1,111 @@
<template>
<!-- 顶部背景 -->
<view class="nav-container">
<view class="title"> 我的 </view>
</view>
<!-- 用户信息 -->
<view class="user-info-box">
<view class="user-icon">
<image src="https://img.smartadmin.1024lab.net/hexagon/logo.png" class="user-image"> </image>
</view>
<view class="user-info">
<view class="user-name">{{ actualName }}</view>
<view class="user-phone">{{ departmentName }}</view>
</view>
<view class="vip-flag">
<image src="/@/static/images/mine/no-vip-flag.png" mode=""></image>
</view>
</view>
</template>
<script setup>
import { useUserStore } from '/@/store/modules/system/user';
import { computed } from 'vue';
const actualName = computed(() => {
return useUserStore().actualName;
});
const phone = computed(() => {
return useUserStore().phone;
});
const departmentName = computed(() => {
return useUserStore().departmentName;
});
</script>
<style scoped lang="scss">
.nav-container {
flex-shrink: 0;
width: 100%;
height: 200rpx;
background: url('@/static/images/mine/top-background.png') center/100% no-repeat;
position: relative;
.title {
text-align: center;
margin-top: 30rpx;
font-size: 36rpx;
color: #fff;
font-weight: bold;
}
}
.user-info-box {
z-index: 10;
margin: -60rpx 12px 0;
background: #ffffff;
border-radius: 6px;
box-shadow: 0px 3px 4px 0px rgba(24, 144, 255, 0.06);
backdrop-filter: blur(5.98px);
padding: 40rpx 30rpx;
display: flex;
flex-direction: row;
align-items: center;
.user-icon {
width: 120rpx;
height: 120rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-radius: 60rpx;
background-color: white;
.user-image {
width: 108rpx;
height: 108rpx;
border-radius: 54rpx;
}
}
.user-info {
display: flex;
flex-direction: column;
align-items: flex-start;
flex-shrink: 0;
margin: 10rpx auto 0 24rpx;
align-self: flex-start;
.user-name {
height: 50rpx;
font-size: 40rpx;
font-weight: 600;
text-align: center;
color: #323333;
}
.user-phone {
margin-top: 8rpx;
height: 40rpx;
font-size: 30rpx;
font-weight: 400;
text-align: left;
color: #777777;
line-height: 40rpx;
}
}
.vip-flag {
align-self: flex-start;
width: 170rpx;
height: 60rpx;
margin: 6rpx 0 0 auto;
image {
width: 100%;
height: 100%;
}
}
}
</style>

View File

@ -0,0 +1,175 @@
<template>
<view class="user-info-box">
<view class="user-icon">
<image class="user-image" src="https://img.smartadmin.1024lab.net/hexagon/logo.png"> </image>
</view>
<view class="user-info">
<view class="user-name">{{ actualName }}</view>
<view class="user-phone">{{ departmentName }}</view>
</view>
</view>
<view class="vip-card">
<view class="card-left">
<image class="vip-icon" src="/static/images/mine/vip-icon.png" mode=""></image>
<view class=""> SmartAdmin </view>
</view>
<image class="open-vip" src="/static/images/mine/open-vip.png" mode=""></image>
</view>
<view class="grid-menu">
<uni-grid :column="4" :showBorder="false">
<uni-grid-item v-for="(item, index) in menuList" :index="index" :key="index">
<view class="grid-item-box" style="background-color: #fff">
<image class="image" :src="item.image" mode=""></image>
<text class="text">{{ item.title }}</text>
</view>
</uni-grid-item>
</uni-grid>
</view>
</template>
<script setup>
import { useUserStore } from '/@/store/modules/system/user';
import { computed } from 'vue';
const actualName = computed(() => {
return useUserStore().actualName;
});
const phone = computed(() => {
return useUserStore().phone;
});
const departmentName = computed(() => {
return useUserStore().departmentName;
});
const menuList = [
{
title: '地址',
image: '/static/images/mine/mine-menu-address.png',
},
{
title: '优惠券',
image: '/static/images/mine/mine-menu-coupon.png',
},
{
title: '收藏',
image: '/static/images/mine/mine-menu-collect.png',
},
{
title: '余额',
image: '/static/images/mine/mine-menu-balance.png',
},
];
</script>
<style scoped lang="scss">
.user-info-box {
z-index: 10;
padding: 60rpx 24rpx 30rpx;
display: flex;
flex-direction: row;
align-items: center;
.user-icon {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
border-radius: 60rpx;
background-color: white;
.user-image {
width: 120rpx;
height: 120rpx;
border-radius: 54rpx;
}
}
.user-info {
display: flex;
flex-direction: column;
align-items: flex-start;
flex-shrink: 0;
margin: 10rpx auto 0 24rpx;
align-self: flex-start;
.user-name {
height: 50rpx;
font-size: 40rpx;
font-weight: 600;
text-align: center;
color: #323333;
}
.user-phone {
margin-top: 8rpx;
height: 40rpx;
font-size: 30rpx;
font-weight: 400;
text-align: left;
color: #777777;
line-height: 40rpx;
}
}
}
.vip-card {
background-image: url('/@/static/images/mine/vip-bg.png');
height: 80rpx;
background-repeat: no-repeat;
width: 700rpx;
margin: 0 auto;
background-size: 700rpx 80rpx;
display: flex;
align-items: center;
justify-content: space-between;
.card-left {
display: flex;
align-items: center;
.vip-icon {
width: 50rpx;
height: 46rpx;
margin-left: 32rpx;
margin-right: 8rpx;
}
view {
color: #f5cc8f;
font-size: 30rpx;
font-weight: 600;
}
}
.open-vip {
width: 136rpx;
height: 46rpx;
display: flex;
margin-right: 32rpx;
}
}
.grid-menu {
width: 700rpx;
margin: 20rpx auto;
}
.menu-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.item-image {
width: 80rpx;
height: 80rpx;
}
}
.image {
width: 84rpx;
height: 84rpx;
}
.text {
font-size: 30rpx;
margin-top: 10rpx;
}
.grid-item-box {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 30rpx 0;
}
</style>

View File

@ -0,0 +1,66 @@
<template>
<view class="container">
<!-- 用户 -->
<MineUserBlue v-if="blueUserFlag" />
<MineUserWhite v-if="!blueUserFlag" />
<!---功能菜单--->
<MineMenu @change-style="onChangeStyle" />
<!---退出--->
<view class="logout-btn" @click="logout">
<view class="label">退出登录</view>
</view>
</view>
</template>
<script setup>
import MineMenu from './components/mine-menu.vue';
import MineUserBlue from './components/mine-user-blue.vue';
import MineUserWhite from './components/mine-user-white.vue';
import { ref } from 'vue';
import { useUserStore } from '/@/store/modules/system/user';
import { SmartLoading, SmartToast } from '/@/lib/smart-support';
import { smartSentry } from '/@/lib/smart-sentry';
const userStore = useUserStore();
const blueUserFlag = ref(true);
function onChangeStyle() {
blueUserFlag.value = !blueUserFlag.value;
}
async function logout() {
try {
setTimeout(() => {
userStore.logout();
uni.navigateTo({ url: '/pages/login/login' });
}, 500);
await SmartLoading.show();
SmartToast.toast('退出成功');
} catch (e) {
smartSentry.captureError(e);
} finally {
SmartLoading.hide();
}
}
</script>
<style lang="scss" scoped>
.container {
width: 100%;
background: #f4f4f4;
display: flex;
flex-direction: column;
}
.logout-btn {
margin: 24px 27px 50px;
height: 44px;
opacity: 0.5;
background: $uni-color-error;
border-radius: 22px;
font-size: 15px;
line-height: 44px;
font-weight: 700;
text-align: center;
color: white;
}
</style>

View File

@ -0,0 +1,72 @@
<template>
<view class="list-container">
<view class="list-item" @click="gotoDetail(item.noticeId)" v-for="item in list" :key="item.noticeId">
<view class="title"> {{ item.noticeTypeName }} {{ item.title }} </view>
<view class="author" v-if="item.author"> 作者{{ item.author }} </view>
<view class="author" v-if="item.source"> 来源{{ item.source }} </view>
<view class="publish-time">
<view class="time">发布时间{{ item.publishTime }}</view>
<view class="un-read" v-if="!item.viewFlag">未读</view>
<view class="read" v-if="item.viewFlag">已读</view>
</view>
</view>
</view>
</template>
<script setup>
const props = defineProps({
marginTop: {
type: String,
default: '0',
},
list: {
type: Array,
default: [],
},
});
function gotoDetail(noticeId) {
uni.navigateTo({ url: '/pages/notice/notice-detail?noticeId=' + noticeId });
}
</script>
<style lang="scss" scoped>
.list-container {
margin-top: v-bind(marginTop);
padding: 20rpx 10rpx;
}
.list-item {
box-sizing: border-box;
margin: 0 30rpx;
padding: 30rpx 0;
border-bottom: #cdcdcd 1px solid;
.title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
font-size: 16px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.author {
margin-bottom: 14rpx;
color: #999999;
font-size: 12px;
text-overflow: ellipsis;
}
.publish-time {
font-size: 12px;
color: #999999;
display: flex;
flex-direction: row;
justify-content: space-between;
.un-read {
color: $uni-color-primary;
}
}
}
</style>

View File

@ -0,0 +1,115 @@
<template>
<uni-popup ref="popupRef" background-color="#fff" type="bottom" :is-mask-click="false">
<view class="query-form-pop">
<view class="smart-form">
<uni-forms :label-width="100" :modelValue="form" label-position="left">
<view class="smart-form-group">
<view class="smart-form-group-title"> 分类 </view>
<view class="smart-form-group-content">
<uni-forms-item class="smart-form-item" label="类型:">
<uni-data-select v-model="form.noticeTypeId" :localdata="noticeTypeList" @change="changeNoticeType" :clear="true" />
</uni-forms-item>
</view>
</view>
<view class="smart-form-group">
<view class="smart-form-group-title"> 发布日期 </view>
<view class="smart-form-group-content">
<uni-forms-item class="smart-form-item" label="开始日期">
<uni-datetime-picker type="date" clear-icon v-model="form.publishTimeBegin" />
</uni-forms-item>
<uni-forms-item class="smart-form-item" label="截止日期">
<uni-datetime-picker type="date" clear-icon v-model="form.publishTimeEnd" />
</uni-forms-item>
</view>
</view>
</uni-forms>
<view class="smart-form-submit smart-margin-top20">
<button class="smart-form-submit-btn smart-margin-right20" type="default" @click="cancel">取消</button>
<button class="smart-form-submit-btn" type="warn" @click="reset">重置</button>
<button class="smart-form-submit-btn" type="primary" @click="ok">确定</button>
</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { onMounted, reactive, ref, toRaw } from 'vue';
import { noticeApi } from '/@/api/business/oa/notice-api';
import _ from 'lodash';
const emits = defineEmits(['close']);
defineExpose({ show });
// --------------------- ---------------------
const popupRef = ref();
function show() {
popupRef.value.open();
}
function close() {
popupRef.value.close();
}
// --------------------- ---------------------
const defaultFormData = {
//
noticeTypeId: undefined,
noticeTypeName: undefined,
//
publishTimeBegin: undefined,
//
publishTimeEnd: undefined,
};
const form = reactive({ ...defaultFormData });
// --------------------- ---------------------
const noticeTypeList = ref([]);
async function queryNoticeTypeList() {
let res = await noticeApi.getAllNoticeTypeList();
noticeTypeList.value = res.data.map((e) => Object.assign({}, { text: e.noticeTypeName, value: e.noticeTypeId }));
}
onMounted(() => {
queryNoticeTypeList();
});
function changeNoticeType(e) {
form.noticeTypeId = e;
form.noticeTypeName = _.find(noticeTypeList.value, { value: form.noticeTypeId }).text;
}
// ----------------------- ------------------------
//
function cancel() {
close();
emits('close', null);
}
//
function reset() {
Object.assign(form, defaultFormData);
close();
emits('close', toRaw(form));
}
//
function ok() {
close();
emits('close', toRaw(form));
}
</script>
<style lang="scss" scoped>
.query-form-pop {
height: 800rpx;
overflow-y: scroll;
}
</style>

View File

@ -0,0 +1,76 @@
<template>
<view class="container">
<view class="title">
<uni-title type="h1" align="center" :title="noticeDetail.title"></uni-title>
<uni-title
type="h4"
align="center"
color="#999999"
v-if="noticeDetail.documentNumber"
:title="'(' + noticeDetail.documentNumber + ')'"
></uni-title>
<uni-title type="h4" align="center" color="#999999" :title="noticeDetail.subTitle"></uni-title>
</view>
<view class="content">
<rich-text :nodes="noticeDetail.content" />
</view>
</view>
</template>
<script setup>
import { reactive } from 'vue';
import { noticeApi } from '/@/api/business/oa/notice-api';
import { onLoad } from '@dcloudio/uni-app';
import { smartSentry } from '/@/lib/smart-sentry';
const noticeDetail = reactive({
title: '',
subTitle: '',
documentNumber: '',
content: '',
});
async function getNoticeDetail(noticeId) {
try {
uni.showLoading({ title: '加载中' });
let res = await noticeApi.view(noticeId);
noticeDetail.title = res.data.title;
noticeDetail.content = res.data.contentHtml;
noticeDetail.documentNumber = res.data.documentNumber;
let subTitleArray = [];
if (res.data.author) {
subTitleArray.push(res.data.author);
}
if (res.data.publishTime) {
subTitleArray.push(res.data.publishTime);
}
noticeDetail.subTitle = subTitleArray.join(' | ');
} catch (e) {
smartSentry.captureError(e);
} finally {
uni.hideLoading();
}
}
onLoad((option) => {
uni.pageScrollTo({
scrollTop: 0,
});
getNoticeDetail(option.noticeId);
});
</script>
<style lang="scss" scoped>
.container {
height: 100vh;
padding: 20rpx;
.content {
border-top: #cccccc 1px solid;
margin-top: 50rpx;
padding: 50rpx 16rpx 50rpx 16rpx;
line-height: 30px;
color: #333333;
}
}
</style>

View File

@ -0,0 +1,224 @@
<template>
<view>
<mescroll-body @init="mescrollInit" :down="{ auto: false }" @down="onDown" @up="onUp">
<!--搜索框-->
<uni-nav-bar :border="false" fixed :leftWidth="0" rightWidth="70px">
<view class="input">
<uni-easyinput
prefixIcon="search"
:clearable="true"
trim="all"
v-model="queryForm.keywords"
placeholder="搜索:标题、作者、来源等"
@confirm="search"
@clear="search"
/>
</view>
<template #right>
<view class="nav-right" @click="showQueryFormPopUp">
<uni-icons type="settings" size="30"></uni-icons>
<view class="nav-right-name"> 筛选 </view>
</view>
</template>
</uni-nav-bar>
<!--筛选条件提示-->
<uni-notice-bar
@close="onCloseQueryFormTips"
v-if="showQueryFormTipsFlag"
class="query-bar"
background-color="#007aff"
color="white"
show-close
single
:text="queryFormTips"
/>
<!-- 筛选条件表单弹窗 -->
<NoticeQueryFormPopUp ref="noticeQueryFormPopUpRef" @close="onCloseQueryFormPopUp" />
<!-- 列表 -->
<NoticeList :list="noticeListData" :margin-top="queryFormTipsMarginTop" />
</mescroll-body>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue';
import NoticeQueryFormPopUp from './components/notice-query-form-popup.vue';
import { noticeApi } from '/@/api/business/oa/notice-api';
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app';
import useMescroll from '@/uni_modules/uni-mescroll/hooks/useMescroll';
import { smartSentry } from '/@/lib/smart-sentry';
import NoticeList from './components/notice-list.vue';
import _ from 'lodash';
// --------------------------- ---------------------------------
const noticeQueryFormPopUpRef = ref();
/**
* 显示 筛选弹窗
*/
function showQueryFormPopUp() {
noticeQueryFormPopUpRef.value.show();
}
/**
* 监听 筛选弹窗 关闭
*/
function onCloseQueryFormPopUp(param) {
if (param === null) {
return;
}
Object.assign(queryForm, param);
showOrHideQueryFormTips();
query(getMescroll(), true, buildQueryParam(1));
uni.pageScrollTo({
scrollTop: 0,
});
}
// --------------------------- tips ---------------------------------
const queryFormTips = ref(null);
const showQueryFormTipsFlag = ref(false);
const queryFormTipsMarginTop = ref('0px');
/**
* 查询提示
*/
function buildQueryFormTips() {
let tips = null;
if (queryForm.keywords) {
tips = '搜索:' + queryForm.keywords;
}
if (queryForm.noticeTypeName) {
tips = tips ? tips + '' : '';
tips = tips + '类型:' + queryForm.noticeTypeName;
}
if (queryForm.publishTimeBegin) {
tips = tips ? tips + '' : '';
tips = tips + '发布开始时间:' + queryForm.publishTimeBegin;
}
if (queryForm.publishTimeEnd) {
tips = tips ? tips + '' : '';
tips = tips + '发布截止时间:' + queryForm.publishTimeEnd;
}
return tips;
}
/**
* 显示或者隐藏tips
*/
function showOrHideQueryFormTips() {
let tips = buildQueryFormTips();
queryFormTipsMarginTop.value = _.isEmpty(tips) ? '0px' : '50rpx';
showQueryFormTipsFlag.value = !_.isEmpty(tips);
queryFormTips.value = tips;
}
/**
* 关闭筛选条件 tips清空搜索条件
*/
function onCloseQueryFormTips() {
Object.assign(queryForm, defaultForm);
queryFormTipsMarginTop.value = '0px';
showQueryFormTipsFlag.value = false;
queryFormTips.value = '';
search();
}
// --------------------------- ---------------------------------
const defaultForm = {
noticeTypeId: undefined, //
noticeTypeName: undefined, //
keywords: '', //
publishTimeBegin: null, //-
publishTimeEnd: null, //-
pageNum: 1,
pageSize: 10,
};
//
const queryForm = reactive({ ...defaultForm });
//
const noticeListData = ref([]);
function buildQueryParam(pageNum) {
queryForm.pageNum = pageNum;
return Object.assign({}, queryForm, { pageNum });
}
async function query(mescroll, isDownFlag, param) {
try {
let res = await noticeApi.queryEmployeeNotice(param);
if (!isDownFlag) {
noticeListData.value = noticeListData.value.concat(res.data.list);
} else {
noticeListData.value = res.data.list;
}
mescroll.endSuccess(res.data.list.length, res.data.pages > res.data.pageNum);
} catch (e) {
smartSentry.captureError(e);
//,
mescroll.endErr();
}
}
const { mescrollInit, getMescroll } = useMescroll(onPageScroll, onReachBottom);
/**
* 搜索
*/
function search() {
showOrHideQueryFormTips();
query(getMescroll(), true, buildQueryParam(1));
uni.pageScrollTo({
scrollTop: 0,
});
}
/**
* 下拉刷新
*/
function onDown(mescroll) {
queryForm.pageNum = 1;
query(mescroll, true, buildQueryParam(1));
}
/**
* 上拉加载更多
*/
function onUp(mescroll) {
query(mescroll, false, buildQueryParam(mescroll.num));
}
</script>
<style lang="scss" scoped>
.input {
width: 100%;
height: 72rpx;
background: #f7f8f9;
border-radius: 4px;
margin: 8rpx 0;
display: flex;
align-items: center;
}
.nav-right {
width: 140px;
display: flex;
height: 88rpx;
flex-direction: row;
line-height: 88rpx;
.nav-right-name {
margin-left: 5px;
line-height: 88rpx;
font-size: 30rpx;
}
}
.query-bar {
position: fixed;
}
</style>

View File

@ -0,0 +1,118 @@
<template>
<view class="smart-detail-card">
<view class="smart-detail-card-title"> 物流信息</view>
<view class="view-item">
<view class="title">
<view class="title-icon-text" type="default"> </view>
<view class="title-content"> 河南省洛阳市洛龙区龙门石窟</view>
</view>
<view class="sub-title"> 河南省洛阳市洛龙区开元大道1024号1024大厦10层1024室 </view>
<view class="sub-title"> 2023-11-23 12:12:12快递员刘备188123123123</view>
</view>
<view class="view-item">
<view class="title">
<view class="title-icon-text" type="warning"> </view>
<view class="title-content"> 山东省济南市高新区齐鲁软件园 </view>
</view>
<view class="sub-title"> 山东省济南市高新区六边形大楼10层</view>
<view class="sub-title"> 快递员卓大 188123123123</view>
</view>
<view class="view-item">
<view class="title">
<view class="title-icon-text" type="error"> </view>
<view class="title-content"> 河南省郑州市郑东新区龙子湖大厦 </view>
</view>
<view class="sub-title"> 河南省郑州市郑东新区高铁路120号 </view>
</view>
<view class="view-item">
<view class="title">
<view class="title-icon-text" type="success"> </view>
<view class="title-content"> 河南省洛阳市洛龙区白马寺 胡克 </view>
</view>
<view class="sub-title"> 河南省洛阳市洛龙区119号 </view>
</view>
</view>
</template>
<script setup></script>
<style lang="scss" scoped>
.line {
position: absolute;
height: 100px;
border-left: 1rpx dashed #444;
z-index: 0;
top: 40rpx;
}
.view-item {
margin-top: 20px;
position: relative;
z-index: 1;
:before {
content: '';
position: absolute;
left: 20rpx;
right: 0;
top: 44rpx;
height: 100%;
width: 1px;
background: #eeeeee;
}
.sub-title {
font-size: 28rpx;
color: #777777;
margin-left: 58rpx;
margin-top: 10px;
}
.title {
display: flex;
align-items: center;
}
.title-content {
font-size: 32rpx;
color: #444;
font-weight: bold;
padding-bottom: 5rpx;
flex: 1;
}
.title-icon-text {
width: 40rpx;
height: 40rpx;
border-radius: 12rpx;
text-align: center;
line-height: 40rpx;
color: #fff;
font-size: 26rpx;
margin-right: 18rpx;
&[type='default'] {
background: #1a9aff;
}
&[type='warning'] {
background: #f3c35b;
}
&[type='error'] {
background: #ff6c00;
}
&[type='success'] {
background: #6ec98a;
}
&:last-child {
margin-bottom: 0;
}
}
}
.view-item:last-child {
:before {
content: '';
position: absolute;
left: 20rpx;
right: 0;
top: 44rpx;
height: 100%;
width: 0;
}
}
</style>

View File

@ -0,0 +1,32 @@
<template>
<view class="smart-detail-card smart-margin-top60">
<view class="smart-detail-card-title"> 商品</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 商品名称 </view>
<view class="smart-detail-card-value"> 华为MateBook 14</view>
</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 价格 </view>
<view class="smart-detail-card-value uni-error">12,345 </view>
</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 颜色/产地 </view>
<view class="smart-detail-card-value">玫瑰金/中国·洛阳 </view>
</view>
<view class="smart-detail-card-cell">
<view class="smart-detail-card-label"> 材质/电压 </view>
<view class="smart-detail-card-value">金属/220V </view>
</view>
</view>
</template>
<script setup>
const props = defineProps({
List: {
type: Array,
default: [],
},
});
</script>
<style lang="scss" scoped></style>

View File

@ -0,0 +1,102 @@
<template>
<view class="smart-detail-card">
<view class="smart-detail-card-title"> 结算信息 </view>
<view class="view-item">
<view class="item-left"> 订单号 </view>
<view class="item-right"> TYD2023080979 </view>
</view>
<view class="view-item">
<view class="item-left"> 付款状态 </view>
<view class="item-right">
<view class="success"> 已审核 </view>
</view>
</view>
<view class="view-item">
<view class="item-left"> 应付金额 </view>
<view class="item-right"> 500 </view>
</view>
<view class="view-item">
<view class="item-left"> 已付金额 </view>
<view class="item-right"> 200 </view>
</view>
<view class="view-item">
<view class="item-left"> 收款账户 </view>
<view class="item-right"> 项羽/61248348910384 </view>
</view>
</view>
</template>
<script setup></script>
<style lang="scss" scoped>
.view {
width: 94%;
background: #fff;
border-radius: 16rpx;
box-sizing: border-box;
padding: 0 30rpx 24rpx;
margin: 0 auto 24rpx;
}
.view-title {
height: 84rpx;
display: flex;
align-items: center;
margin-bottom: 16rpx;
.title-left-bg {
height: 28rpx;
width: 6rpx;
background: #1a9aff;
border-radius: 4rpx;
margin-right: 14rpx;
}
.title-text {
font-size: 32rpx;
color: #323333;
font-weight: bold;
}
.sub-title {
flex: 1;
display: flex;
justify-content: flex-end;
view {
height: 42rpx;
width: 112rpx;
text-align: center;
line-height: 42rpx;
font-size: 24rpx;
margin-left: 24rpx;
}
.success {
background: #e5f8e9;
color: #1ce36d;
}
.warning {
background: #fff0ed;
color: #ff6c00;
}
}
}
.view-item {
display: flex;
height: 40rpx;
align-items: center;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
.item-left {
width: 180rpx;
margin-right: 20rpx;
font-size: 28rpx;
color: #777777;
}
.item-right {
font-size: 30rpx;
color: #323333;
font-weight: bold;
font-size: 28rpx;
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More