This commit is contained in:
eleanor.mao
2025-06-03 00:03:08 +08:00
commit 48f2c49c2b
100 changed files with 34596 additions and 0 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

8
.eslintignore Normal file
View File

@ -0,0 +1,8 @@
/lambda/
/scripts
/config
.history
public
dist
.umi
mock

28
.eslintrc.js Normal file
View File

@ -0,0 +1,28 @@
module.exports = {
extends: [require.resolve('@umijs/lint/dist/config/eslint')],
globals: {
page: true,
REACT_APP_ENV: true,
},
plugins: ['import'],
rules: {
'import/order': [
'error',
{
groups: ['builtin', 'external', ['internal', 'parent', 'sibling', 'index'], 'unknown'],
pathGroups: [
{
pattern: '@/**',
group: 'external',
position: 'after',
},
],
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true,
},
},
],
},
};

41
.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
**/node_modules
# roadhog-api-doc ignore
/src/utils/request-temp.js
_roadhog-api-doc
# production
/dist
# misc
.DS_Store
npm-debug.log*
yarn-error.log
/coverage
.idea
yarn.lock
package-lock.json
*bak
.vscode
# visual studio code
.history
*.log
functions/*
.temp/**
# umi
.umi
.umi-production
.umi-test
# screenshot
screenshot
.firebase
.eslintcache
build

7
.husky/commit-msg Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Export Git hook params
export GIT_PARAMS=$*
npx --no-install fabric verify-commit

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install lint-staged

22
.prettierignore Normal file
View File

@ -0,0 +1,22 @@
**/*.svg
.umi
.umi-production
/dist
.dockerignore
.DS_Store
.eslintignore
*.png
*.toml
docker
.editorconfig
Dockerfile*
.gitignore
.prettierignore
LICENSE
.eslintcache
*.lock
yarn-error.log
.history
CNAME
/build
/public

30
.prettierrc.js Normal file
View File

@ -0,0 +1,30 @@
module.exports = {
singleQuote: true,
trailingComma: 'all',
printWidth: 120,
proseWrap: 'preserve',
endOfLine: 'lf',
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
arrowParens: 'avoid',
bracketSpacing: true,
htmlWhitespaceSensitivity: 'strict',
jsxBracketSameLine: false,
jsxSingleQuote: false,
overrides: [
{
files: '.prettierrc',
options: {
parser: 'json',
},
},
{
files: 'document.ejs',
options: {
parser: 'html',
},
},
],
};

35
README.md Normal file
View File

@ -0,0 +1,35 @@
## Environment Prepare
```bash
yarn
```
### Start project
```bash
yarn start
```
### Build project
```bash
yarn build
```
### Check code style
```bash
yarn lint
```
You can also use script to auto fix some lint error:
```bash
yarn lint:fix
```
### Test code
```bash
yarn test
```

148
config/config.ts Normal file
View File

@ -0,0 +1,148 @@
// https://umijs.org/config/
import { defineConfig } from '@umijs/max';
import { join } from 'path';
import defaultSettings from './defaultSettings';
import proxy from './proxy';
import routes from './routes';
const { REACT_APP_ENV = 'dev' } = process.env;
export default defineConfig({
/**
* @name 开启 hash 模式
* @description 让 build 之后的产物包含 hash 后缀。通常用于增量发布和避免浏览器加载缓存。
* @doc https://umijs.org/docs/api/config#hash
*/
hash: true,
/**
* @name 兼容性设置
* @description 设置 ie11 不一定完美兼容,需要检查自己使用的所有依赖
* @doc https://umijs.org/docs/api/config#targets
*/
// targets: {
// ie: 11,
// },
/**
* @name 路由的配置,不在路由中引入的文件不会编译
* @description 只支持 pathcomponentroutesredirectwrapperstitle 的配置
* @doc https://umijs.org/docs/guides/routes
*/
// umi routes: https://umijs.org/docs/routing
routes,
/**
* @name 主题的配置
* @description 虽然叫主题,但是其实只是 less 的变量设置
* @doc antd的主题设置 https://ant.design/docs/react/customize-theme-cn
* @doc umi 的theme 配置 https://umijs.org/docs/api/config#theme
*/
theme: {
// 如果不想要 configProvide 动态设置主题需要把这个设置为 default
// 只有设置为 variable 才能使用 configProvide 动态设置主色调
'root-entry-name': 'variable',
},
/**
* @name moment 的国际化配置
* @description 如果对国际化没有要求打开之后能减少js的包大小
* @doc https://umijs.org/docs/api/config#ignoremomentlocale
*/
ignoreMomentLocale: true,
/**
* @name 代理配置
* @description 可以让你的本地服务器代理到你的服务器上,这样你就可以访问服务器的数据了
* @see 要注意以下 代理只能在本地开发时使用build 之后就无法使用了。
* @doc 代理介绍 https://umijs.org/docs/guides/proxy
* @doc 代理配置 https://umijs.org/docs/api/config#proxy
*/
proxy: proxy[REACT_APP_ENV as keyof typeof proxy],
/**
* @name 快速热更新配置
* @description 一个不错的热更新组件,更新时可以保留 state
*/
fastRefresh: true,
//============== 以下都是max的插件配置 ===============
/**
* @name 数据流插件
* @@doc https://umijs.org/docs/max/data-flow
*/
model: {},
/**
* 一个全局的初始数据流,可以用它在插件之间共享数据
* @description 可以用来存放一些全局的数据,比如用户信息,或者一些全局的状态,全局初始状态在整个 Umi 项目的最开始创建。
* @doc https://umijs.org/docs/max/data-flow#%E5%85%A8%E5%B1%80%E5%88%9D%E5%A7%8B%E7%8A%B6%E6%80%81
*/
initialState: {},
/**
* @name layout 插件
* @doc https://umijs.org/docs/max/layout-menu
*/
title: '播络管理后台',
layout: {
locale: true,
...defaultSettings,
},
/**
* @name moment2dayjs 插件
* @description 将项目中的 moment 替换为 dayjs
* @doc https://umijs.org/docs/max/moment2dayjs
*/
moment2dayjs: {
preset: 'antd',
plugins: ['duration'],
},
/**
* @name 国际化插件
* @doc https://umijs.org/docs/max/i18n
*/ /**
* @name antd 插件
* @description 内置了 babel import 插件
* @doc https://umijs.org/docs/max/antd#antd
*/
antd: {},
/**
* @name 网络请求配置
* @description 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
* @doc https://umijs.org/docs/max/request
*/
request: {},
/**
* @name 权限插件
* @description 基于 initialState 的权限插件,必须先打开 initialState
* @doc https://umijs.org/docs/max/access
*/
access: {},
/**
* @name <head> 中额外的 script
* @description 配置 <head> 中额外的 script
*/
headScripts: [
// 解决首次加载时白屏的问题
{
src: '/scripts/loading.js',
async: true,
},
],
//================ pro 插件配置 =================
presets: ['umi-presets-pro'],
/**
* @name openAPI 插件的配置
* @description 基于 openapi 的规范生成serve 和mock能减少很多样板代码
* @doc https://pro.ant.design/zh-cn/docs/openapi/
*/
openAPI: [
{
requestLibPath: "import { request } from '@umijs/max'",
// 或者使用在线的版本
// schemaPath: "https://gw.alipayobjects.com/os/antfincdn/M%24jrzTTYJN/oneapi.json"
schemaPath: join(__dirname, 'oneapi.json'),
mock: false,
},
{
requestLibPath: "import { request } from '@umijs/max'",
schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json',
projectName: 'swagger',
},
],
mfsu: {
strategy: 'normal',
},
esbuildMinifyIIFE: true,
requestRecord: {},
});

28
config/defaultSettings.ts Normal file
View File

@ -0,0 +1,28 @@
import { ProLayoutProps } from '@ant-design/pro-components';
/**
* @name
*/
const Settings: ProLayoutProps & {
pwa?: boolean;
logo?: string;
} = {
navTheme: 'light',
// 拂晓蓝
colorPrimary: '#1890ff',
layout: 'mix',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
title: '播络管理后台',
pwa: true,
logo: 'https://wx.qlogo.cn/mmhead/K6CEv0Hv9DeKqL5Tvpg854VHlzYmMOAXrzdiaNMib5nydicmyz8FV3UhfWWFI0Qczavcr4zSTEPbE0/0',
iconfontUrl: '',
token: {
// 参见ts声明demo 见文档通过token 修改样式
//https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F
},
};
export default Settings;

15
config/oneapi.json Normal file
View File

@ -0,0 +1,15 @@
{
"openapi": "3.0.1",
"info": {
"title": "boluo",
"version": "1.0.0"
},
"servers": [
{
"url": "http://localhost:8000/"
},
{
"url": "https://localhost:8000/"
}
]
}

44
config/proxy.ts Normal file
View File

@ -0,0 +1,44 @@
/**
* @name 代理的配置
* @see 在生产环境 代理是无法生效的,所以这里没有生产环境的配置
* -------------------------------
* The agent cannot take effect in the production environment
* so there is no configuration of the production environment
* For details, please see
* https://pro.ant.design/docs/deploy
*
* @doc https://umijs.org/docs/guides/proxy
*/
export default {
// 如果需要自定义本地开发服务器 请取消注释按需调整
// dev: {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
// '/api/': {
// // 要代理的地址
// target: 'http://10.147.18.100:8082',
// // 配置了这个可以从 http 代理到 https
// // 依赖 origin 的功能可能需要这个,比如 cookie
// changeOrigin: true,
// },
// },
mako: {},
/**
* @name 详细的代理配置
* @doc https://github.com/chimurai/http-proxy-middleware
*/
test: {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
'/api/': {
target: 'https://proapi.azurewebsites.net',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
pre: {
'/api/': {
target: 'your pre url',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
};

16
config/routes.ts Normal file
View File

@ -0,0 +1,16 @@
export default [
{
path: '/user',
layout: false,
routes: [{ name: '登录', path: '/user/login', component: './user/login' }],
},
{ name: '通告管理', icon: 'table', path: '/list/job', component: './table-list/job' },
{ name: '群管理', icon: 'table', path: '/list/group', component: './table-list/group' },
{ name: '主播申请进群管理', icon: 'table', path: '/list/anchor-group', component: './table-list/anchor-group' },
{ name: '职位发布人管理', icon: 'table', path: '/list/publisher', component: './table-list/publisher' },
{ name: '用户管理', icon: 'table', path: '/list/anchor', component: './table-list/anchor' },
{ name: '主播报单管理', icon: 'table', path: '/list/declaration', component: './table-list/declaration' },
{ name: '模卡管理', icon: 'table', path: '/list/resume', component: './table-list/material' },
{ path: '/', redirect: '/list/job' },
{ path: '*', layout: false, component: './404' },
];

View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>模卡录屏</title>
<style>
body {
padding: 0;
margin: 0;
background: #000;
}
video{
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<script type="application/javascript">
var search = window.location.search;
var searchParams = new URLSearchParams(search);
var decodeSource = searchParams.get('source');
if (decodeSource) {
var videoHtml = document.createElement('video');
videoHtml.setAttribute('src', decodeSource);
videoHtml.setAttribute('autoplay', true);
videoHtml.setAttribute('controls', true);
videoHtml.setAttribute('playsinline', true);
document.body.appendChild(videoHtml);
}
</script>
</body>
</html>

128
html/protocol.html Normal file
View File

@ -0,0 +1,128 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>小程序隐私保护指引</title>
<style>
/* 通用样式 */
body {
font-family: "Arial", sans-serif;
line-height: 1.8;
color: #333;
background-color: #f9f9f9;
margin: 0;
padding: 0;
}
header {
background-color: #007BFF;
color: #fff;
text-align: center;
padding: 20px 10px;
}
header h1 {
font-size: 1.6em;
margin: 0;
}
main {
padding: 20px 15px;
background-color: #fff;
margin: 10px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
h2 {
color: #007BFF;
font-size: 1.2em;
margin: 20px 0 10px;
}
p {
font-size: 0.95em;
margin: 10px 0;
}
ul {
margin: 10px 0 20px 20px;
padding: 0;
list-style-type: disc;
}
ul li {
margin-bottom: 10px;
}
footer {
text-align: center;
font-size: 0.85em;
color: #666;
padding: 10px;
margin-top: 20px;
}
/* 响应式优化 */
@media (max-width: 768px) {
body {
font-size: 16px;
}
header h1 {
font-size: 1.4em;
}
main {
margin: 10px 5px;
padding: 15px 10px;
}
}
</style>
</head>
<body>
<header>
<h1>小程序隐私保护指引</h1>
</header>
<main>
<p>本指引是<b>播络l每天推荐海量高薪主播通告小程序</b>开发者杭州播络科技有限公司(以下简称“开发者”)为处理你的个人信息而制定。</p>
<h2>1. 开发者处理的信息</h2>
<p>根据法律规定,开发者仅处理实现小程序功能所必要的信息。</p>
<p><b>以下为所有用户均可能收集的信息:</b></p>
<ul>
<li>为了“招聘各方及时获得通知”,开发者将在获取你的明示同意后,收集你的手机号。</li>
<li>为了“方便招聘双方取得联系”,开发者将在获取你的明示同意后,收集你的微信号。</li>
<li>为了“让您的个人信息显得真实,更好获得求职双方青睐”,开发者将在获取你的明示同意后,收集你的微信昵称、头像。</li>
<li>为了“向您展示附近的求职者或者招聘信息”,开发者将在获取你的明示同意后,收集你的位置信息。</li>
</ul>
<p><b>以下为求职者会收集的信息:</b></p>
<ul>
<li>为了吸引招聘方对您发出职位邀请,开发者将在获取你的明示同意后,收集你主动上传的视频、照片、个人介绍信息。</li>
</ul>
<p><b>以下为招聘者会收集的信息:</b></p>
<ul>
<li>为了保证求职者的权益,避免不法分子对求职者造成骚扰和欺骗,需要通过第三方(阿里云)的实人认证接口对招聘者进行真实身份验证,开发者将在获取你的明示同意后,收集你的姓名、身份证号、手机号信息。</li>
<li>为了保证招聘信息的真实性,开发者将在获取你的明示同意后,收集招聘公司的公司名称、营业执照、法人名称等。</li>
</ul>
<h2>2. 你的权益</h2>
<p>关于收集你的位置信息,你可以通过以下路径:<b>小程序主页右上角“...”—“设置”—点击特定信息—点击“不允许”</b>,撤回对开发者的授权。</p>
<p>关于你的个人信息,你可以通过以下方式与开发者联系,行使查阅、复制、更正、删除等法定权利。</p>
<p>若你在小程序中注册了账号,你可以通过以下方式与开发者联系,申请注销你在小程序中使用的账号。在受理你的申请后,开发者承诺在十五个工作日内完成核查和处理,并按照法律法规要求处理你的相关信息。</p>
<p><b>邮箱:</b>xiaochengxu@neighbourhood.com.cn</p>
<h2>3. 信息的存储</h2>
<p>开发者承诺,除法律法规另有规定外,开发者对你的信息的保存期限应当为实现处理目的所必要的最短时间。</p>
<h2>4. 信息的使用规则</h2>
<p>开发者将会在本指引所明示的用途内使用收集的信息;</p>
<p>如开发者使用你的信息超出本指引目的或合理范围,开发者必须在变更使用目的或范围前,再次以电子邮件方式告知并征得你的明示同意。</p>
<h2>5. 信息对外提供</h2>
<p><b>第三方合作伙伴:</b></p>
<p>我们可能会与第三方合作伙伴共享您的信息,以实现特定功能,但仅在招聘用途范围内进行。</p>
<p><b>法律要求:</b></p>
<p>在法律要求的情况下,我们可能会向执法机构或其他相关方提供您的信息。</p>
<p>开发者承诺,不会在招聘用途外公开披露你的信息,如必须公开披露时,开发者应当向你告知公开披露的目的、披露信息的类型及可能涉及的信息,并征得你的单独同意。</p>
<h2>6. 投诉与建议</h2>
<p>你认为开发者未遵守上述约定,或有其他的投诉建议、或未成年人个人信息保护相关问题,可通过以下方式与开发者联系:</p>
<p><b>微信号:</b>xiaochengxu@neighbourhood.com.cn</p>
</main>
<footer>
<p>&copy; 2025 杭州播络科技有限公司</p>
</footer>
</body>
</html>

133
html/user-agreement.html Normal file
View File

@ -0,0 +1,133 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>用户协议</title>
<style>
body {
font-family: "Arial", sans-serif;
line-height: 1.8;
color: #333;
background-color: #f9f9f9;
margin: 0;
padding: 0;
}
header {
background-color: #007BFF;
color: #fff;
text-align: center;
padding: 20px 10px;
}
header h1 {
font-size: 1.6em;
margin: 0;
}
main {
padding: 20px 15px;
background-color: #fff;
margin: 10px;
border-radius: 10px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
h2 {
color: #007BFF;
font-size: 1.2em;
margin: 20px 0 10px;
}
p {
font-size: 0.95em;
margin: 10px 0;
}
ul {
margin: 10px 0 20px 20px;
padding: 0;
list-style-type: disc;
}
ul li {
margin-bottom: 10px;
}
footer {
text-align: center;
font-size: 0.85em;
color: #666;
padding: 10px;
margin-top: 20px;
}
@media (max-width: 768px) {
body {
font-size: 16px;
}
header h1 {
font-size: 1.4em;
}
main {
margin: 10px 5px;
padding: 15px 10px;
}
}
</style>
</head>
<body>
<header>
<h1>用户协议</h1>
</header>
<main>
<p>欢迎您使用<b>播络主播招聘平台</b>(以下简称 “本平台”)!为了保障您在本平台的合法权益,维护平台的正常秩序,请您在使用本平台服务前,仔细阅读本用户协议(以下简称 “本协议”)的全部内容。如果您对本协议的任何条款表示异议,您可以选择不使用本平台服务。</p>
<h2>一、定义</h2>
<ul>
<li><b>本平台:</b>指由 [杭州播络科技有限公司] 运营的,为用户提供招聘信息发布、求职信息展示、人才匹配等服务的互联网平台。</li>
<li><b>用户:</b>指使用本平台服务的个人或组织,包括求职者和招聘者。</li>
<li><b>个人信息:</b>指以电子或者其他方式记录的能够单独或者与其他信息结合识别特定自然人身份或者反映特定自然人活动情况的各种信息。</li>
</ul>
<h2>二、服务内容</h2>
<p>本平台为求职者提供职位搜索、简历制作与投递、面试通知等服务。</p>
<p>本平台为招聘者提供职位发布、简历筛选、人才邀约等服务。</p>
<p>平台有权根据自身运营情况,对服务内容进行调整和变更。</p>
<h2>三、注册与账号管理</h2>
<p>用户在使用本平台服务前,需完成注册流程,提供真实、准确、完整的个人信息。</p>
<h2>四、用户权利与义务</h2>
<h3>求职者权利义务</h3>
<ul>
<li>有权浏览、搜索并申请符合自身需求的职位。</li>
<li>需保证所提供的简历信息真实有效。</li>
<li>不得恶意投递简历或骚扰招聘者。</li>
<li>不得出于求职以外的目的获取平台招聘者信息。</li>
</ul>
<h3>招聘者权利义务</h3>
<ul>
<li>有权发布真实、合法的招聘信息。</li>
<li>对收到的简历进行妥善处理,不得泄露求职者个人信息。</li>
<li>不得进行虚假招聘或其他欺诈行为。</li>
</ul>
<h2>五、隐私保护</h2>
<p>平台承诺对用户的个人信息严格保密,仅在法律允许或用户授权的情况下使用。</p>
<p>平台可能会将部分用户信息用于数据分析和服务优化,但不会泄露用户的敏感信息。</p>
<h2>六、协议变更</h2>
<p>平台有权根据业务发展和法律法规的变化,对本协议进行变更。变更后的协议将在平台上公布,用户继续使用平台服务即视为同意变更后的协议。</p>
<h2>七、违约责任</h2>
<ul>
<li>用户违反本协议约定的,平台可采取封号等方式限制用户使用,且所付服务费用不予退还。</li>
<li>用户违反本协议约定,给平台或其他用户造成损失的,应承担赔偿责任。</li>
<li>用户对平台服务不满,在未违背平台用户协议的情况下,可在付款后 3 日内申请退款,平台将在扣除已使用服务标准金额后,进行退款。超出 3 日,不予退款。</li>
</ul>
<p>退款申请渠道:<b>xiaochengxu@neighbourhood.com.cn</b></p>
<h2>八、争议解决</h2>
<p>若双方发生争议,应首先友好协商解决;协商不成的,任何一方均有权向杭州播络科技有限公司注册地人民法院提起诉讼。</p>
<p><b>请您仔细阅读并充分理解本协议内容,如您点击 “同意” 按钮或使用本平台服务,即表示您已接受本协议的全部条款。</b></p>
</main>
<footer>
<p>&copy; 2025 杭州播络科技有限公司</p>
</footer>
</body>
</html>

23
jest.config.ts Normal file
View File

@ -0,0 +1,23 @@
import { configUmiAlias, createConfig } from '@umijs/max/test';
export default async () => {
const config = await configUmiAlias({
...createConfig({
target: 'browser',
}),
});
console.log(JSON.stringify(config));
return {
...config,
testEnvironmentOptions: {
...(config?.testEnvironmentOptions || {}),
url: 'http://localhost:8000',
},
setupFiles: [...(config.setupFiles || []), './tests/setupTests.jsx'],
globals: {
...config.globals,
localStorage: null,
},
};
};

11
jsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

365
mock/list.ts Normal file
View File

@ -0,0 +1,365 @@
import { Request, Response } from 'express';
import dayjs from 'dayjs';
import { AdminAPI } from '../src/constants/api';
import { DeclarationType } from '../src/constants/product';
const genJobList = (current: number, pageSize: number) => {
const tableListDataSource: API.JobListItem[] = [];
for (let i = 0; i < pageSize; i += 1) {
const index = (current - 1) * 10 + i;
tableListDataSource.push({
id: `${index}-id`,
jobId: `${index}-id`,
title: '招聘主播',
jobDescription: '招聘主播',
sourceText: '招聘主播',
cityCode: index % 2 === 0 ? '110100' : '430100',
category: 'ALL',
employType: 'ALL',
publisher: '甲子',
blPublisherId: `${index}-blPublisherId`,
publisherAcctNo: `${index}-publisherAcctNo`,
blGroupId: `${index}-blGroupId`,
imGroupId: `${index}-imGroupId`,
imGroupNick: '杭州主播群3',
robotId: `${index}-robotId`,
robotImNo: `${index}-robotImNo`,
robotImNick: `${index}-robotImNick`,
relateGroupCount: 100,
created: Date.now().toString(),
updated: Date.now().toString(),
disable: index % 2 === 0,
});
}
tableListDataSource.reverse();
return tableListDataSource;
};
const genGroupList = (current: number, pageSize: number) => {
const tableListDataSource: API.GroupListItem[] = [];
for (let i = 0; i < pageSize; i += 1) {
const index = (current - 1) * 10 + i;
tableListDataSource.push({
id: `${index}-id`,
imGroupId: `${index}-groupId`,
imGroupNick: '杭州主播群3',
groupType: '',
groupOwnerNick: '甲子',
groupOwnerAcctNo: `${index}-boluoId`,
groupOwnerImAcctNo: `${index}-wxId`,
robotId: `${index}-robotId`,
robotImNo: `${index}-robotImNo`,
robotImNick: `${index}-robotImNick`,
city: index % 2 === 0 ? '110100' : '430100',
jobCount: 10,
// created: dayjs().format('YYYY-MM-DD HH:mm:ss'),
created: Date.now().toString(),
updated: Date.now().toString(),
disable: index % 2 === 0,
isDeleted: index % 2 === 0,
});
}
tableListDataSource.reverse();
return tableListDataSource;
};
const genAnchorGroupList = (current: number, pageSize: number) => {
const tableListDataSource: API.AnchorGroupListItem[] = [];
for (let i = 0; i < pageSize; i += 1) {
const index = (current - 1) * 10 + i;
tableListDataSource.push({
userId: `${index}-id`,
nickName: '甲子',
userPhone: '18222020020',
blGroupId: `${index}-blGroupId`,
imGroupId: `${index}-imGroupId`,
imGroupNick: '杭州主播群3',
creator: '甲子',
payed: index % 2 === 0,
payType: 'wx',
created: Date.now().toString(),
status: 0,
});
}
tableListDataSource.reverse();
return tableListDataSource;
};
const genPublisherList = (current: number, pageSize: number) => {
const tableListDataSource: API.PublisherListItem[] = [];
for (let i = 0; i < pageSize; i += 1) {
const index = (current - 1) * 10 + i;
tableListDataSource.push({
publisher: '甲子',
blPublisherId: `${index}-blPublisherId`,
publisherAcctNo: `${index}-publisherAcctNo`,
publisherAcctStatus: index % 2 === 0 ? 0 : 1,
status: index % 2 === 0 ? 0 : 1,
addAcctStatus: index % 2 === 0 ? 0 : index % 3 === 0 ? 1 : index % 4 === 0 ? 2 : 3,
robotImNick: `${index}-robotImNick`,
phone: `${index}-phone`,
email: `${index}-email`,
operator: '宵夜',
imGroupId: `${index}-imGroupId`,
blGroupId: `${index}-blGroupId`,
imGroupNick: `${index}-imGroupNick`,
created: Date.now().toString(),
updated: Date.now().toString(),
});
}
tableListDataSource.reverse();
return tableListDataSource;
};
const genAnchorList = (current: number, pageSize: number) => {
const tableListDataSource: API.AnchorListItem[] = [];
for (let i = 0; i < pageSize; i += 1) {
const index = (current - 1) * 10 + i;
tableListDataSource.push({
userId: `${index}-id`,
nickName: '甲子',
userPhone: index % 2 === 0 ? '18222020020' : '',
isBindPhone: index % 2 === 0,
created: Date.now().toString(),
lastLoginDate: Date.now().toString(),
status: index % 2 === 0 ? 0 : 1,
city: '400100',
});
}
tableListDataSource.reverse();
return tableListDataSource;
};
const genDeclarationList = (current: number, pageSize: number) => {
const tableListDataSource: API.DeclarationListItem[] = [];
for (let i = 0; i < pageSize; i += 1) {
const index = (current - 1) * 10 + i;
tableListDataSource.push({
id: `${index}-id`,
type: index % 2 === 0 ? DeclarationType.CS : DeclarationType.Direct,
userId: `${index}-userId`,
nickName: '甲子',
userPhone: '18222020020',
jobId: `${index}-id`,
title: '招聘主播',
useDate: Date.now().toString(),
publisher: '甲子',
blPublisherId: `${index}-blPublisherId`,
publisherAcctNo: `${index}-publisherAcctNo`,
declaredStatus: index % 2 === 0 ? 0 : 1,
declarationDate: Date.now().toString(),
declaredDate: Date.now().toString(),
weComStatus: index % 2 === 0 ? 0 : 1,
jobCityCode: '400100',
});
}
tableListDataSource.reverse();
return tableListDataSource;
};
let jobListDataSource = genJobList(1, 100);
let groupListDataSource = genGroupList(1, 100);
let anchorGroupListDataSource = genAnchorGroupList(1, 100);
let publisherListDataSource = genPublisherList(1, 100);
let anchorListDataSource = genAnchorList(1, 100);
let declarationListDataSource = genDeclarationList(1, 100);
function getGroupList(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { page = 1, pageSize = 10 } = body;
const dataSource = [...groupListDataSource].slice(
((page as number) - 1) * (pageSize as number),
(page as number) * (pageSize as number),
);
const result = {
data: dataSource,
total: groupListDataSource.length,
pageSize,
page: parseInt(`${page}`, 10) || 1,
};
// res.status(417).send({
// data: {
// code: 'NEED_LOGIN',
// },
// errorCode: '401',
// errorMessage: '请先登录!',
// });
// return;
return res.json(result);
}
function updateGroup(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { id } = body;
let newRule = {};
groupListDataSource = groupListDataSource.map(item => {
if (item.id === id) {
newRule = { ...item, ...body };
return { ...item, ...body };
}
return item;
});
res.json(newRule);
}
function getJobList(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { page = 1, pageSize = 10 } = body;
const dataSource = [...jobListDataSource].slice(
((page as number) - 1) * (pageSize as number),
(page as number) * (pageSize as number),
);
const result = {
data: dataSource,
total: jobListDataSource.length,
pageSize,
page: parseInt(`${page}`, 10) || 1,
};
return res.json(result);
}
function updateJob(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { jobId } = body;
let newRule = {};
jobListDataSource = jobListDataSource.map(item => {
if (item.jobId === jobId) {
newRule = { ...item, ...body };
return { ...item, ...body };
}
return item;
});
res.json(newRule);
}
function getAnchorGroupList(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { page = 1, pageSize = 10 } = body;
const dataSource = [...anchorGroupListDataSource].slice(
((page as number) - 1) * (pageSize as number),
(page as number) * (pageSize as number),
);
const result = {
data: dataSource,
total: anchorGroupListDataSource.length,
pageSize,
page: parseInt(`${page}`, 10) || 1,
};
return res.json(result);
}
function addAnchorGroup(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
anchorGroupListDataSource.unshift({ ...body, creator: '小叶' });
res.json({ success: true });
}
function getPublisherList(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { page = 1, pageSize = 10 } = body;
const dataSource = [...publisherListDataSource].slice(
((page as number) - 1) * (pageSize as number),
(page as number) * (pageSize as number),
);
const result = {
data: dataSource,
total: publisherListDataSource.length,
pageSize,
page: parseInt(`${page}`, 10) || 1,
};
return res.json(result);
}
function updatePublisher(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { blPublisherId } = body;
let newRule = {};
publisherListDataSource = publisherListDataSource.map(item => {
if (item.blPublisherId === blPublisherId) {
newRule = { ...item, ...body };
return { ...item, ...body };
}
return item;
});
res.json(newRule);
}
function getAnchorList(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { page = 1, pageSize = 10 } = body;
const dataSource = [...anchorListDataSource].slice(
((page as number) - 1) * (pageSize as number),
(page as number) * (pageSize as number),
);
const result = {
data: dataSource,
total: anchorListDataSource.length,
pageSize,
page: parseInt(`${page}`, 10) || 1,
};
return res.json(result);
}
function getDeclarationList(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { page = 1, pageSize = 10 } = body;
const dataSource = [...declarationListDataSource].slice(
((page as number) - 1) * (pageSize as number),
(page as number) * (pageSize as number),
);
const result = {
data: dataSource,
total: declarationListDataSource.length,
pageSize,
page: parseInt(`${page}`, 10) || 1,
};
return res.json(result);
}
function updateDeclaration(req: Request, res: Response, u: string, b: Request) {
const body = (b && b.body) || req.body;
const { id } = body;
let newRule = {};
declarationListDataSource = declarationListDataSource.map(item => {
if (item.id === id) {
newRule = { ...item, ...body };
return { ...item, ...body };
}
return item;
});
res.json(newRule);
}
export default {
[`POST ${AdminAPI.GROUP_LIST}`]: getGroupList,
[`POST ${AdminAPI.GROUP_UPDATE}`]: updateGroup,
[`POST ${AdminAPI.JOB_LIST}`]: getJobList,
[`POST ${AdminAPI.JOB_UPDATE}`]: updateJob,
[`POST ${AdminAPI.ANCHOR_GROUP_LIST}`]: getAnchorGroupList,
[`POST ${AdminAPI.ADD_ANCHOR_GROUP}`]: addAnchorGroup,
[`POST ${AdminAPI.PUBLISHER_LIST}`]: getPublisherList,
[`POST ${AdminAPI.PUBLISHER_UPDATE}`]: updatePublisher,
[`POST ${AdminAPI.ANCHOR_LIST}`]: getAnchorList,
[`POST ${AdminAPI.DECLARATION_LIST}`]: getDeclarationList,
[`POST ${AdminAPI.DECLARATION_UPDATE}`]: updateDeclaration,
};

5
mock/route.ts Normal file
View File

@ -0,0 +1,5 @@
export default {
'/api/auth_routes': {
'/form/advanced-form': { authority: ['admin', 'user'] },
},
};

79
mock/user.ts Normal file
View File

@ -0,0 +1,79 @@
import { Request, Response } from 'express';
import { md5 } from 'js-md5';
import { AdminAPI } from '../src/constants/api';
const waitTime = (time: number = 100) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(true);
}, time);
});
};
const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env;
/**
* 当前用户的权限,如果为空代表没登录
* current user access if is '', user need login
* 如果是 pro 的预览,默认是有权限的
*/
let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : '';
const getAccess = () => {
return access;
};
export default {
[`POST ${AdminAPI.USER}`]: (req: Request, res: Response) => {
if (!getAccess()) {
res.status(401).send({
data: {
isLogin: false,
},
errorCode: '401',
errorMessage: '请先登录!',
success: true,
});
return;
}
res.send({
id: '1',
userId: '00000001',
userName: '小叶',
created: Date.now().toString(),
updated: Date.now().toString(),
});
},
[`POST ${AdminAPI.LOGIN}`]: async (req: Request, res: Response) => {
const { pwd, userName, type } = req.body;
await waitTime(2000);
if (pwd === md5('boluo') && userName === 'admin') {
res.json({
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
expires: 3600,
});
access = 'admin';
return;
}
if (pwd === md5('user') && userName === 'user') {
res.json({
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
expires: 3600,
});
access = 'user';
return;
}
res.send({
status: 'error',
type,
currentAuthority: 'guest',
});
access = 'guest';
},
[`POST ${AdminAPI.OUT_LOGIN}`]: (req: Request, res: Response) => {
access = '';
res.send({ data: {}, success: true });
},
};

100
package.json Normal file
View File

@ -0,0 +1,100 @@
{
"name": "boluo-admin",
"version": "6.0.0",
"private": true,
"description": "boluo admin system",
"scripts": {
"analyze": "cross-env ANALYZE=1 max build",
"build": "rm -rf dist && max build",
"deploy": "npm run build && npm run gh-pages",
"dev": "npm run start:dev",
"gh-pages": "gh-pages -d dist",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
"postinstall": "max setup",
"jest": "jest",
"lint": "npm run lint:js && npm run lint:prettier && npm run tsc",
"lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src ",
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
"lint:prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto",
"openapi": "max openapi",
"prepare": "husky install",
"prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"",
"preview": "npm run build && max preview --port 8000",
"record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login",
"serve": "umi-serve",
"start": "cross-env UMI_ENV=dev max dev",
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev",
"start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev",
"start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev",
"start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
"test": "jest",
"test:coverage": "npm run jest -- --coverage",
"test:update": "npm run jest -- -u",
"tsc": "tsc --noEmit"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
"**/*.{js,jsx,tsx,ts,less,md,json}": [
"prettier --write"
]
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
],
"dependencies": {
"@ant-design/icons": "^4.8.3",
"@ant-design/pro-components": "^2.7.9",
"@umijs/route-utils": "^2.2.2",
"antd": "^5.18.0",
"antd-style": "^3.6.2",
"classnames": "^2.5.1",
"js-md5": "^0.8.3",
"omit.js": "^2.0.2",
"querystring": "^0.2.1",
"rc-menu": "^9.14.0",
"rc-util": "^5.41.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-helmet-async": "^1.3.0"
},
"devDependencies": {
"@ant-design/pro-cli": "^3.3.0",
"@testing-library/react": "^13.4.0",
"@types/classnames": "^2.3.1",
"@types/express": "^4.17.21",
"@types/history": "^4.7.11",
"@types/jest": "^29.5.12",
"@types/lodash": "^4.17.4",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-helmet": "^6.1.11",
"@umijs/fabric": "^2.14.1",
"@umijs/lint": "^4.2.9",
"@umijs/max": "^4.2.9",
"cross-env": "^7.0.3",
"dayjs": "^1.11.13",
"eslint": "^8.57.0",
"eslint-plugin-import": "^2.29.1",
"express": "^4.19.2",
"gh-pages": "^3.2.3",
"husky": "^7.0.4",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"lint-staged": "^10.5.4",
"mockjs": "^1.1.0",
"prettier": "^3.3.2",
"react-dev-inspector": "^1.9.0",
"swagger-ui-dist": "^4.19.1",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"umi-presets-pro": "^2.0.3",
"umi-serve": "^1.9.11"
},
"engines": {
"node": ">=12.0.0"
}
}

25393
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

0
public/CNAME Normal file
View File

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

40
public/logo.svg Normal file
View File

@ -0,0 +1,40 @@
<svg width="119" height="150" viewBox="0 0 119 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M80.175 13.0632C77.7363 14.5724 72.8825 18.5651 70.9547 22.1217C72.101 23.5385 74.2688 26.4282 74.9477 29.151L74.9327 29.1973C74.94 29.1911 74.9473 29.1849 74.9546 29.1787C74.9569 29.188 74.9592 29.1973 74.9614 29.2066L74.9765 29.1603C77.1261 27.3566 80.5784 26.293 82.3386 25.8206C82.8695 21.8101 81.2895 15.7269 80.2037 13.0725L80.2147 13.0387C80.2081 13.0427 80.2015 13.0468 80.1948 13.0509C80.1919 13.0437 80.1889 13.0365 80.186 13.0293L80.175 13.0632Z" fill="url(#paint0_linear_1133_1248)"/>
<rect x="30.068" y="34.5977" width="81" height="95" rx="40" transform="rotate(18 30.068 34.5977)" fill="url(#paint1_linear_1133_1248)"/>
<mask id="mask0_1133_1248" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="11" y="44" width="86" height="96">
<rect x="30.068" y="34.5977" width="81" height="95" rx="40" transform="rotate(18 30.068 34.5977)" fill="#6D3DF5"/>
</mask>
<g mask="url(#mask0_1133_1248)">
<path d="M44.1998 86.6703C37.3495 101.333 53.9573 129.851 56.966 141.123C50.6532 140.738 33.4868 137.658 19.2811 129.03C7.9165 122.127 3.17011 99.692 5.06788 89.3529C7.03047 80.549 14.0224 65.124 32.3405 57.7505C50.6585 50.377 77.9175 62.5192 82.781 63.2914C78.1227 67.8123 51.0501 72.0077 44.1998 86.6703Z" fill="url(#paint2_linear_1133_1248)"/>
<path d="M65.2721 89.672C56.6928 75.9492 31.4296 68.1613 20.334 64.5561C24.0902 59.4678 28.3099 52.9529 43.2763 45.724C55.2494 39.9408 76.6578 48.1585 84.3022 55.3739C90.6231 61.8088 99.7651 76.065 95.991 95.4473C92.2168 114.83 82.3105 126.226 79.0175 129.888L78.9682 129.639C77.657 123.01 73.7425 103.221 65.2721 89.672Z" fill="url(#paint3_linear_1133_1248)"/>
<path d="M54.3243 105.567C72.0134 102.903 82.3998 85.5969 94.1322 68.9053C96.6697 74.6985 99.4948 87.9969 94.0752 103.059C89.5736 115.571 85.9242 123.194 79.3388 130.518C71.3267 137.378 71.2493 137.241 62.0994 139.986C46.9544 144.528 41.9848 104.712 41.8182 103.606C45.9435 105.472 48.3502 106.467 54.3243 105.567Z" fill="url(#paint4_linear_1133_1248)"/>
</g>
<path d="M49.346 78.2787C50.1089 77.5699 51.1851 77.463 52.1429 77.8716C54.5231 78.8868 59.2767 81.3391 63.2464 86.0225C67.068 90.5312 68.8716 94.6597 69.6235 96.7902C69.9625 97.7509 69.8012 98.7972 69.1237 99.558C67.5202 101.359 63.7786 104.576 56.8886 105.614C50.361 106.597 45.798 105.796 43.4931 105.157C42.3728 104.847 41.5843 103.924 41.4146 102.774C41.005 99.9996 40.6797 94.3251 43.2682 88.0447C45.5974 82.3935 47.948 79.5776 49.346 78.2787Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M72.6959 36.1793C73.3511 22.5129 57.676 16.5076 50.0756 15.0896C59.4485 23.5722 57.0116 35.3237 54.4321 40.6567C59.4124 40.307 64.5418 40.8902 69.5817 42.5277L70.5328 42.8367C75.6599 44.5026 80.2205 47.1058 84.0867 50.395L84.8227 50.6341C85.8327 44.8288 90.7533 33.7742 103.392 32.4133C96.41 29.0931 80.1988 24.7378 72.6959 36.1793Z" fill="url(#paint5_linear_1133_1248)"/>
<defs>
<linearGradient id="paint0_linear_1133_1248" x1="79.7104" y1="12.8748" x2="74.4572" y2="29.0428" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFA564"/>
<stop offset="1" stop-color="#FF8064"/>
</linearGradient>
<linearGradient id="paint1_linear_1133_1248" x1="100.568" y1="73.5977" x2="83.9796" y2="125.245" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D3DF5"/>
<stop offset="1" stop-color="#3D65F5"/>
</linearGradient>
<linearGradient id="paint2_linear_1133_1248" x1="43.6429" y1="140.834" x2="67.3536" y2="51.3927" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D3DF5"/>
<stop offset="1" stop-color="#5A87FC"/>
</linearGradient>
<linearGradient id="paint3_linear_1133_1248" x1="25.9803" y1="58.5047" x2="78.4685" y2="99.217" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D3DF5"/>
<stop offset="1" stop-color="#5A87FC"/>
</linearGradient>
<linearGradient id="paint4_linear_1133_1248" x1="105.072" y1="87.7149" x2="38.3481" y2="87.1272" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D3DF5"/>
<stop offset="1" stop-color="#5A87FC"/>
</linearGradient>
<linearGradient id="paint5_linear_1133_1248" x1="58.4025" y1="17.4824" x2="51.1823" y2="39.7037" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFA564"/>
<stop offset="1" stop-color="#FF8064"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

5
public/pro_icon.svg Normal file
View File

@ -0,0 +1,5 @@
<svg width="42" height="42" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill="#070707" d="m6.717392,13.773912l5.6,0c2.8,0 4.7,1.9 4.7,4.7c0,2.8 -2,4.7 -4.9,4.7l-2.5,0l0,4.3l-2.9,0l0,-13.7zm2.9,2.2l0,4.9l1.9,0c1.6,0 2.6,-0.9 2.6,-2.4c0,-1.6 -0.9,-2.4 -2.6,-2.4l-1.9,0l0,-0.1zm8.9,11.5l2.7,0l0,-5.7c0,-1.4 0.8,-2.3 2.2,-2.3c0.4,0 0.8,0.1 1,0.2l0,-2.4c-0.2,-0.1 -0.5,-0.1 -0.8,-0.1c-1.2,0 -2.1,0.7 -2.4,2l-0.1,0l0,-1.9l-2.7,0l0,10.2l0.1,0zm11.7,0.1c-3.1,0 -5,-2 -5,-5.3c0,-3.3 2,-5.3 5,-5.3s5,2 5,5.3c0,3.4 -1.9,5.3 -5,5.3zm0,-2.1c1.4,0 2.2,-1.1 2.2,-3.2c0,-2 -0.8,-3.2 -2.2,-3.2c-1.4,0 -2.2,1.2 -2.2,3.2c0,2.1 0.8,3.2 2.2,3.2z" class="st0" id="Ant-Design-Pro"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 677 B

202
public/scripts/loading.js Normal file
View File

@ -0,0 +1,202 @@
/**
* loading 占位
* 解决首次加载时白屏的问题
*/
(function () {
const _root = document.querySelector('#root');
if (_root && _root.innerHTML === '') {
_root.innerHTML = `
<style>
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
}
#root {
background-repeat: no-repeat;
background-size: 100% auto;
}
.loading-title {
font-size: 1.1rem;
}
.loading-sub-title {
margin-top: 20px;
font-size: 1rem;
color: #888;
}
.page-loading-warp {
display: flex;
align-items: center;
justify-content: center;
padding: 26px;
}
.ant-spin {
position: absolute;
display: none;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
color: #1890ff;
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
text-align: center;
list-style: none;
opacity: 0;
-webkit-transition: -webkit-transform 0.3s
cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: -webkit-transform 0.3s
cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
-webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
-webkit-font-feature-settings: "tnum";
font-feature-settings: "tnum";
}
.ant-spin-spinning {
position: static;
display: inline-block;
opacity: 1;
}
.ant-spin-dot {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
font-size: 20px;
}
.ant-spin-dot-item {
position: absolute;
display: block;
width: 9px;
height: 9px;
background-color: #1890ff;
border-radius: 100%;
-webkit-transform: scale(0.75);
-ms-transform: scale(0.75);
transform: scale(0.75);
-webkit-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
transform-origin: 50% 50%;
opacity: 0.3;
-webkit-animation: antspinmove 1s infinite linear alternate;
animation: antSpinMove 1s infinite linear alternate;
}
.ant-spin-dot-item:nth-child(1) {
top: 0;
left: 0;
}
.ant-spin-dot-item:nth-child(2) {
top: 0;
right: 0;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.ant-spin-dot-item:nth-child(3) {
right: 0;
bottom: 0;
-webkit-animation-delay: 0.8s;
animation-delay: 0.8s;
}
.ant-spin-dot-item:nth-child(4) {
bottom: 0;
left: 0;
-webkit-animation-delay: 1.2s;
animation-delay: 1.2s;
}
.ant-spin-dot-spin {
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-animation: antrotate 1.2s infinite linear;
animation: antRotate 1.2s infinite linear;
}
.ant-spin-lg .ant-spin-dot {
width: 32px;
height: 32px;
font-size: 32px;
}
.ant-spin-lg .ant-spin-dot i {
width: 14px;
height: 14px;
}
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.ant-spin-blur {
background: #fff;
opacity: 0.5;
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 1;
}
}
@keyframes antSpinMove {
to {
opacity: 1;
}
}
@-webkit-keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
</style>
<div style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
min-height: 362px;
">
<div class="page-loading-warp">
<div class="ant-spin ant-spin-lg ant-spin-spinning">
<span class="ant-spin-dot ant-spin-dot-spin">
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
</span>
</div>
</div>
<div class="loading-title">
正在加载资源
</div>
<div class="loading-sub-title">
初次加载资源可能需要较多时间 请耐心等待
</div>
</div>
`;
}
})();

9
src/access.ts Normal file
View File

@ -0,0 +1,9 @@
/**
* @see https://umijs.org/docs/max/access#access
* */
export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) {
const { currentUser } = initialState ?? {};
return {
canAdmin: !!currentUser,
};
}

122
src/app.tsx Normal file
View File

@ -0,0 +1,122 @@
import { LinkOutlined } from '@ant-design/icons';
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
import { SettingDrawer } from '@ant-design/pro-components';
import type { RunTimeLayoutConfig } from '@umijs/max';
import { Link, history } from '@umijs/max';
import Footer from '@/components/footer';
import { AvatarDropdown, AvatarName } from '@/components/right-content/avatar-dropdown';
import { LOGIN_PATH } from '@/constants/global';
import { requestConfig } from '@/requestConfig';
import { currentUser as queryCurrentUser } from '@/services/user';
import { gotoLogin } from '@/utils/login';
import defaultSettings from '../config/defaultSettings';
const isDev = process.env.NODE_ENV === 'development';
/**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */
export async function getInitialState(): Promise<{
settings?: Partial<LayoutSettings>;
currentUser?: API.CurrentUser;
loading?: boolean;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> {
const fetchUserInfo = async () => {
try {
const userInfo = await queryCurrentUser({
skipErrorHandler: true,
});
return userInfo;
} catch (error) {
gotoLogin();
}
return undefined;
};
// 如果不是登录页面,执行
const { location } = history;
if (location.pathname !== LOGIN_PATH) {
const currentUser = await fetchUserInfo();
return {
fetchUserInfo,
currentUser,
settings: defaultSettings as Partial<LayoutSettings>,
};
}
return {
fetchUserInfo,
settings: defaultSettings as Partial<LayoutSettings>,
};
}
// ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
return {
actionsRender: () => [],
avatarProps: {
src:
initialState?.currentUser?.avatar ||
'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
title: <AvatarName />,
render: (_, avatarChildren) => {
return <AvatarDropdown>{avatarChildren}</AvatarDropdown>;
},
},
waterMarkProps: {
content: initialState?.currentUser?.userName,
},
footerRender: () => <Footer />,
onPageChange: () => {
const { location } = history;
// 如果没有登录,重定向到 login
if (!initialState?.currentUser && location.pathname !== LOGIN_PATH) {
gotoLogin();
}
},
links: isDev
? [
<Link key="openapi" to="/umi/plugin/openapi" target="_blank">
<LinkOutlined />
<span>OpenAPI </span>
</Link>,
]
: [],
menuHeaderRender: undefined,
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
// 增加一个 loading 的状态
childrenRender: children => {
// if (initialState?.loading) return <PageLoading />;
return (
<>
{children}
{isDev && (
<SettingDrawer
disableUrlParams
enableDarkTheme
settings={initialState?.settings}
onSettingChange={settings => {
setInitialState(preInitialState => ({
...preInitialState,
settings,
}));
}}
/>
)}
</>
);
},
...initialState?.settings,
};
};
/**
* @name request 配置,可以配置错误处理
* 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
* @doc https://umijs.org/docs/max/request#配置
*/
export const request = {
...requestConfig,
};

View File

@ -0,0 +1,13 @@
import { DefaultFooter } from '@ant-design/pro-components';
import React from 'react';
const Footer: React.FC = () => {
return (
<DefaultFooter
style={{ background: 'none' }}
copyright="杭州播络科技有限公司"
/>
);
};
export default Footer;

View File

@ -0,0 +1,27 @@
import { Dropdown } from 'antd';
import type { DropDownProps } from 'antd/es/dropdown';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import React from 'react';
const useStyles = createStyles(({ token }) => {
return {
dropdown: {
[`@media screen and (max-width: ${token.screenXS}px)`]: {
width: '100%',
},
},
};
});
export type HeaderDropdownProps = {
overlayClassName?: string;
placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter';
} & Omit<DropDownProps, 'overlay'>;
const HeaderDropdown: React.FC<HeaderDropdownProps> = ({ overlayClassName: cls, ...restProps }) => {
const { styles } = useStyles();
return <Dropdown overlayClassName={classNames(styles.dropdown, cls)} {...restProps} />;
};
export default HeaderDropdown;

View File

@ -0,0 +1,49 @@
import { Image } from 'antd';
import { createStyles } from 'antd-style';
import { ReactElement } from 'react';
interface IProps {
videos: API.MaterialVideoInfo[];
}
const useStyles = createStyles(() => {
return {
container: {
width: 330,
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
marginLeft: '-10px',
marginBottom: '-10px',
},
imageContainer: {
marginLeft: '10px',
marginBottom: '10px',
},
};
});
const Previewer = (props: IProps) => {
const { videos = [] } = props;
const { styles } = useStyles();
const imageRender = (originalNode: ReactElement, info: { current: number }) => {
const video = videos[info.current];
if (!video || video.type === 'image') {
return originalNode;
}
// console.log('============>>>>>>', info);
return <video controls autoPlay width="400px" src={video.url} />;
};
return (
<div className={styles.container}>
<Image.PreviewGroup preview={{ imageRender, toolbarRender: () => null, destroyOnClose: true }}>
{videos.map(video => (
<Image key={video.coverUrl} width={100} src={video.coverUrl} wrapperClassName={styles.imageContainer} />
))}
</Image.PreviewGroup>
</div>
);
};
export default Previewer;

View File

@ -0,0 +1,125 @@
import { stringify } from 'querystring';
import { LogoutOutlined } from '@ant-design/icons';
import { history, useModel } from '@umijs/max';
import { Spin } from 'antd';
import { createStyles } from 'antd-style';
import type { MenuInfo } from 'rc-menu/lib/interface';
import React, { useCallback } from 'react';
import { flushSync } from 'react-dom';
import { outLogin } from '@/services/user';
import { clearToken } from '@/utils/login';
import HeaderDropdown from '../header-dropdown';
export type GlobalHeaderRightProps = {
menu?: boolean;
children?: React.ReactNode;
};
export const AvatarName = () => {
const { initialState } = useModel('@@initialState');
const { currentUser } = initialState || {};
return <span className="anticon">{currentUser?.userName}</span>;
};
const useStyles = createStyles(({ token }) => {
return {
action: {
display: 'flex',
height: '48px',
marginLeft: 'auto',
overflow: 'hidden',
alignItems: 'center',
padding: '0 8px',
cursor: 'pointer',
borderRadius: token.borderRadius,
'&:hover': {
backgroundColor: token.colorBgTextHover,
},
},
};
});
export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ children }) => {
/**
* 退出登录,并且将当前的 url 保存
*/
const loginOut = async () => {
await outLogin();
clearToken();
const { search, pathname } = window.location;
const urlParams = new URL(window.location.href).searchParams;
/** 此方法会跳转到 redirect 参数所在的位置 */
const redirect = urlParams.get('redirect');
// Note: There may be security issues, please note
if (window.location.pathname !== '/user/login' && !redirect) {
history.replace({
pathname: '/user/login',
search: stringify({
redirect: pathname + search,
}),
});
}
};
const { styles } = useStyles();
const { initialState, setInitialState } = useModel('@@initialState');
const onMenuClick = useCallback(
(event: MenuInfo) => {
const { key } = event;
if (key === 'logout') {
flushSync(() => {
setInitialState(s => ({ ...s, currentUser: undefined }));
});
loginOut();
return;
}
},
[setInitialState],
);
const loading = (
<span className={styles.action}>
<Spin
size="small"
style={{
marginLeft: 8,
marginRight: 8,
}}
/>
</span>
);
if (!initialState) {
return loading;
}
const { currentUser } = initialState;
if (!currentUser || !currentUser.userName) {
return loading;
}
const menuItems = [
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
},
];
return (
<HeaderDropdown
menu={{
selectedKeys: [],
onClick: onMenuClick,
items: menuItems,
}}
>
{children}
</HeaderDropdown>
);
};

4
src/constants/anchor.ts Normal file
View File

@ -0,0 +1,4 @@
export enum AnchorStatusType {
Normal = 0,
Forbid = 1,
}

28
src/constants/api.ts Normal file
View File

@ -0,0 +1,28 @@
export enum AdminAPI {
// 用户级别
LOGIN = '/api/bo/adminUser/login', // 登录
OUT_LOGIN = '/api/bo/adminUser/outLogin',
USER = '/api/bo/adminUser/get',
// 群
GROUP_LIST = '/api/bo/imGroup/list',
GROUP_UPDATE = '/api/bo/imGroup/update',
// 通告
JOB_LIST = '/api/bo/job/list',
JOB_UPDATE = '/api/bo/job/update',
// 定制群
ANCHOR_GROUP_LIST = '/api/bo/myGroup/list',
ADD_ANCHOR_GROUP = '/api/bo/product/addGroup',
// 职位发布人
PUBLISHER_LIST = '/api/bo/publisher/list',
PUBLISHER_DECLARATION_LIST = '/api/bo/publisher/listWithDeclaration',
PUBLISHER_UPDATE = '/api/bo/publisher/update',
// 主播
ANCHOR_LIST = '/api/bo/user/list',
ANCHOR_UPDATE = '/api/bo/user/update',
// 主播报单(解锁)
DECLARATION_LIST = '/api/bo/declaration/list',
DECLARATION_UPDATE = '/api/bo/declaration/update',
// 模卡
MATERIAL_LIST = '/api/bo/resume/list',
MATERIAL_UPDATE = '/api/bo/resume/update',
}

3908
src/constants/city.ts Normal file

File diff suppressed because it is too large Load Diff

2
src/constants/global.ts Normal file
View File

@ -0,0 +1,2 @@
export const LOGIN_PATH = '/user/login';
export const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';

19
src/constants/http.ts Normal file
View File

@ -0,0 +1,19 @@
export enum HTTP_STATUS {
SUCCESS = 200,
FAIL = 417,
}
export enum RESPONSE_ERROR_CODE {
INVALID_PARAMETER = 'INVALID_PARAMETER',
NOT_FUND = 'NOT_FUND',
INNER_EXCEPTION = 'INNER_EXCEPTION',
SYSTEM_ERROR = 'SYSTEM_ERROR',
NEED_LOGIN = 'NEED_LOGIN',
}
export const RESPONSE_ERROR_MESSAGE = {
[RESPONSE_ERROR_CODE.INVALID_PARAMETER]: '参数无效',
[RESPONSE_ERROR_CODE.NOT_FUND]: '找到对应资源',
[RESPONSE_ERROR_CODE.INNER_EXCEPTION]: '服务内部错误',
[RESPONSE_ERROR_CODE.SYSTEM_ERROR]: '系统错误',
};

24
src/constants/job.ts Normal file
View File

@ -0,0 +1,24 @@
export enum JobType {
All = 'ALL',
Finery = 'FINERY', // 服饰
Makeups = 'MAKEUPS', // 美妆
Digital = 'DIGITAL', //数码
Foods = 'FOODS', // 食品酒饮
Jewelry = 'JEWELRY', // 珠宝
Appliance = 'APPLIANCE', // 家电
Furniture = 'FURNITURE', // 日用家具
PetFamily = 'PET_FAMILY', // 母婴宠物
Luxury = 'LUXURY', // 奢品
}
export enum EmployType {
All = 'ALL',
Full = 'FULL_TIME',
Part = 'PARTY_TIME',
}
export const EMPLOY_OPTIONS = [
{ label: '全职', value: EmployType.Full },
{ label: '兼职', value: EmployType.Part },
{ label: '不限', value: EmployType.All },
];

37
src/constants/material.ts Normal file
View File

@ -0,0 +1,37 @@
export enum WorkedYears {
LessOneYear = 0.5,
OneYear = 1,
TwoYear = 2,
MoreThreeYear = 3,
}
export enum GenderType {
MEN = 0,
WOMEN = 1,
}
// 1主播主动创建 2主播填写表单创建 3 运营人工创建 4 机器人创建
export enum ProfileCreateSource {
User = 1,
UserInput = 2,
Bl = 3,
Robot = 4,
}
export enum StyleType {
Broadcasting = 1,
HoldOrder = 2,
Passion = 3,
}
export enum MaterialStatus {
Open = 0,
Close = 1,
}
export const WORK_YEAR_LABELS = {
[WorkedYears.LessOneYear]: '1 年以下',
[WorkedYears.OneYear]: '1 年',
[WorkedYears.TwoYear]: '2 年',
[WorkedYears.MoreThreeYear]: '3 年以上',
};

13
src/constants/product.ts Normal file
View File

@ -0,0 +1,13 @@
export enum PayType {
Free = 'free',
VX = 'vx',
AliPay = 'alipay',
Other = 'other',
}
export enum DeclarationType {
// 直接连接通告主
Direct = 0,
// 客服联系 customer service
CS = 1,
}

52
src/global.less Normal file
View File

@ -0,0 +1,52 @@
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}
.colorWeak {
filter: invert(80%);
}
.ant-layout {
min-height: 100vh;
}
.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed {
left: unset;
}
canvas {
display: block;
}
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
ul,
ol {
list-style: none;
}
@media (max-width: 768px) {
.ant-table {
width: 100%;
overflow-x: auto;
&-thead > tr,
&-tbody > tr {
> th,
> td {
white-space: pre;
> span {
display: block;
}
}
}
}
}

93
src/global.tsx Normal file
View File

@ -0,0 +1,93 @@
import '@umijs/max';
import { Button, message, notification } from 'antd';
import defaultSettings from '../config/defaultSettings';
const { pwa } = defaultSettings;
const isHttps = document.location.protocol === 'https:';
const clearCache = () => {
// remove all caches
if (window.caches) {
caches
.keys()
.then(keys => {
keys.forEach(key => {
caches.delete(key);
});
})
.catch(e => console.log(e));
}
};
// if pwa is true
if (pwa) {
// Notify user if offline now
window.addEventListener('sw.offline', () => {
message.warning('当前处于离线状态');
});
// Pop up a prompt on the page asking the user if they want to use the latest version
window.addEventListener('sw.updated', (event: Event) => {
const e = event as CustomEvent;
const reloadSW = async () => {
// Check if there is sw whose state is waiting in ServiceWorkerRegistration
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
const worker = e.detail && e.detail.waiting;
if (!worker) {
return true;
}
// Send skip-waiting event to waiting SW with MessageChannel
await new Promise((resolve, reject) => {
const channel = new MessageChannel();
channel.port1.onmessage = msgEvent => {
if (msgEvent.data.error) {
reject(msgEvent.data.error);
} else {
resolve(msgEvent.data);
}
};
worker.postMessage(
{
type: 'skip-waiting',
},
[channel.port2],
);
});
clearCache();
window.location.reload();
return true;
};
const key = `open${Date.now()}`;
const btn = (
<Button
type="primary"
onClick={() => {
notification.destroy(key);
reloadSW();
}}
>
{'刷新'}
</Button>
);
notification.open({
message: '有新内容',
description: '请点击“刷新”按钮或者手动刷新页面',
btn,
key,
onClose: async () => null,
});
});
} else if ('serviceWorker' in navigator && isHttps) {
// unregister service worker
const { serviceWorker } = navigator;
if (serviceWorker.getRegistrations) {
serviceWorker.getRegistrations().then(sws => {
sws.forEach(sw => {
sw.unregister();
});
});
}
serviceWorker.getRegistration().then(sw => {
if (sw) sw.unregister();
});
clearCache();
}

22
src/manifest.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "boluo",
"short_name": "boluo",
"display": "standalone",
"start_url": "./?utm_source=homescreen",
"theme_color": "#002140",
"background_color": "#001529",
"icons": [
{
"src": "icons/icon-192x192.png",
"sizes": "192x192"
},
{
"src": "icons/icon-128x128.png",
"sizes": "128x128"
},
{
"src": "icons/icon-512x512.png",
"sizes": "512x512"
}
]
}

16
src/pages/404.tsx Normal file
View File

@ -0,0 +1,16 @@
import { history } from '@umijs/max';
import { Button, Result } from 'antd';
import React from 'react';
const NoFoundPage: React.FC = () => (
<Result
status="404"
title="404"
subTitle={'抱歉,您访问的页面不存在。'}
extra={
<Button type="primary" onClick={() => history.push('/')}>
{'返回首页'}
</Button>
}
/>
);
export default NoFoundPage;

View File

@ -0,0 +1,95 @@
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { PageContainer, ProTable } from '@ant-design/pro-components';
import '@umijs/max';
import { Select } from 'antd';
import dayjs from 'dayjs';
import React, { useRef } from 'react';
import { TIME_FORMAT } from '@/constants/global';
import { getAnchorGroupList } from '@/services/list';
const STATUS_OPTIONS = [
{ label: '已申请', value: 0 },
{ label: '已申请未进群 ', value: 1 },
{ label: '已进群 ', value: 2 },
];
const TableList: React.FC = () => {
const actionRef = useRef<ActionType>();
const columns: ProColumns<API.AnchorGroupListItem>[] = [
{
title: '主播昵称',
dataIndex: 'nickName',
valueType: 'textarea',
search: false,
},
{
title: '主播ID',
dataIndex: 'userId',
valueType: 'textarea',
copyable: true,
},
{
title: '主播手机号',
dataIndex: 'userPhone',
valueType: 'textarea',
copyable: true,
},
{
title: '群ID',
dataIndex: 'blGroupId',
valueType: 'textarea',
copyable: true,
},
{
title: '群名称',
dataIndex: 'imGroupNick',
valueType: 'textarea',
search: false,
copyable: true,
},
{
title: '加群状态',
dataIndex: 'status',
valueType: 'textarea',
renderText(status: number) {
return STATUS_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
},
{
title: '绑定时间',
dataIndex: 'created',
valueType: 'dateTime',
renderText(created: string) {
return dayjs(Number(created)).format(TIME_FORMAT);
},
search: false,
// search: {
// transform: (created: string) => dayjs(created).valueOf().toString(),
// },
},
{
title: '绑定人',
dataIndex: 'creator',
valueType: 'textarea',
copyable: true,
},
];
return (
<PageContainer>
<ProTable<API.AnchorGroupListItem, API.PageParams>
headerTitle="查询表格"
actionRef={actionRef}
rowKey="key"
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
request={getAnchorGroupList}
columns={columns}
/>
</PageContainer>
);
};
export default TableList;

View File

@ -0,0 +1,147 @@
import type { ActionType, ProColumns, ProFormInstance } from '@ant-design/pro-components';
import { ModalForm, PageContainer, ProFormSelect, ProTable } from '@ant-design/pro-components';
import '@umijs/max';
import { Select } from 'antd';
import dayjs from 'dayjs';
import React, { useRef, useState } from 'react';
import { CITY_CODE_TO_NAME_MAP, CITY_OPTIONS } from '@/constants/city';
import { TIME_FORMAT } from '@/constants/global';
import { getAnchorList, updateAnchorInfo } from '@/services/list';
const STATUS_OPTIONS = [
{ label: '正常', value: 0 },
{ label: '封禁', value: 1 },
];
const TableList: React.FC = () => {
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
const [currentRow, setCurrentRow] = useState<API.AnchorListItem>();
const actionRef = useRef<ActionType>();
const formRef = useRef<ProFormInstance>();
const columns: ProColumns<API.AnchorListItem>[] = [
{
title: '主播昵称',
dataIndex: 'nickName',
valueType: 'textarea',
},
{
title: '主播ID',
dataIndex: 'userId',
valueType: 'textarea',
copyable: true,
},
{
title: '主播手机号',
dataIndex: 'userPhone',
valueType: 'textarea',
copyable: true,
},
{
title: '注册时间',
dataIndex: 'created',
valueType: 'dateTime',
renderText(created: string) {
return dayjs(Number(created)).format(TIME_FORMAT);
},
search: false,
// search: {
// transform: (created: string) => dayjs(created).valueOf().toString(),
// },
},
{
title: '最后登录时间',
dataIndex: 'lastLoginDate',
valueType: 'textarea',
search: false,
renderText(created: string) {
return dayjs(Number(created)).format(TIME_FORMAT);
},
},
{
title: '账号状态',
dataIndex: 'status',
valueType: 'textarea',
renderText(status: number) {
return STATUS_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
},
{
title: '城市',
dataIndex: 'city',
valueType: 'textarea',
renderText(cityCode: string) {
return CITY_CODE_TO_NAME_MAP.get(cityCode);
},
renderFormItem() {
return (
<Select
showSearch
allowClear
options={CITY_OPTIONS}
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
/>
);
},
},
{
title: '操作',
valueType: 'option',
render: (_, record) => (
<a
key="config"
onClick={() => {
handleUpdateModalOpen(true);
setCurrentRow(record);
}}
>
</a>
),
},
];
return (
<PageContainer>
<ProTable<API.AnchorListItem, API.PageParams>
headerTitle="查询表格"
actionRef={actionRef}
rowKey="key"
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
request={getAnchorList}
columns={columns}
/>
<ModalForm
title="更新主播信息"
width="400px"
formRef={formRef}
open={updateModalOpen}
onOpenChange={handleUpdateModalOpen}
onFinish={async data => {
const params: API.UpdateAnchorParams = {
userId: currentRow!.userId,
status: data.status,
};
console.log('update confirm', data, params);
try {
await updateAnchorInfo(params);
actionRef.current?.reload();
formRef.current?.resetFields();
} catch (e) {}
handleUpdateModalOpen(false);
}}
>
<ProFormSelect
name="status"
label="账号状态"
options={STATUS_OPTIONS}
rules={[{ required: true, message: '必填项' }]}
/>
</ModalForm>
</PageContainer>
);
};
export default TableList;

View File

@ -0,0 +1,228 @@
import type { ActionType, ProColumns, ProFormInstance } from '@ant-design/pro-components';
import { ModalForm, PageContainer, ProFormSelect, ProFormTextArea, ProTable } from '@ant-design/pro-components';
import '@umijs/max';
import { Select } from 'antd';
import dayjs from 'dayjs';
import React, { useRef, useState } from 'react';
import { CITY_CODE_TO_NAME_MAP, CITY_OPTIONS } from '@/constants/city';
import { TIME_FORMAT } from '@/constants/global';
import { DeclarationType } from '@/constants/product';
import { getDeclarationList, updateDeclarationInfo } from '@/services/list';
const STATUS_OPTIONS = [
{ label: '待处理', value: 0 },
{ label: '已处理', value: 1 },
];
const TYPE_OPTIONS = [
{ label: '直接联系', value: DeclarationType.Direct },
{ label: '客服联系', value: DeclarationType.CS },
];
const WE_COM_OPTIONS = [
{ label: '未加', value: 0 },
{ label: '已加', value: 1 },
];
const TableList: React.FC = () => {
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
const [currentRow, setCurrentRow] = useState<API.DeclarationListItem>();
const actionRef = useRef<ActionType>();
const formRef = useRef<ProFormInstance>();
const columns: ProColumns<API.DeclarationListItem>[] = [
{
title: '主播昵称',
dataIndex: 'nickName',
valueType: 'textarea',
},
{
title: '主播ID',
dataIndex: 'userId',
valueType: 'textarea',
copyable: true,
},
{
title: '主播手机号',
dataIndex: 'userPhone',
valueType: 'textarea',
copyable: true,
},
{
title: '通告标题',
dataIndex: 'title',
valueType: 'textarea',
},
{
title: '通告ID',
dataIndex: 'jobId',
valueType: 'textarea',
copyable: true,
},
{
title: '解锁时间',
dataIndex: 'useDate',
valueType: 'dateTime',
renderText(time: string) {
return dayjs(Number(time)).format(TIME_FORMAT);
},
search: false,
},
{
title: '职位发布人昵称',
dataIndex: 'publisher',
valueType: 'textarea',
search: false,
copyable: true,
},
{
title: '职位发布人ID',
dataIndex: 'blPublisherId',
valueType: 'textarea',
copyable: true,
},
{
title: '职位发布人微信',
dataIndex: 'publisherAcctNo',
valueType: 'textarea',
copyable: true,
},
{
title: '报单类型',
dataIndex: 'type',
valueType: 'textarea',
renderText(type: number) {
return TYPE_OPTIONS.find(option => option.value === type)?.label;
},
renderFormItem() {
return <Select allowClear options={TYPE_OPTIONS} />;
},
},
{
title: '报单时间',
dataIndex: 'declarationDate',
valueType: 'dateTime',
renderText(time: string) {
return dayjs(Number(time)).format(TIME_FORMAT);
},
search: false,
},
{
title: '报单处理状态',
dataIndex: 'declaredStatus',
valueType: 'textarea',
renderText(status: number) {
const declaredStatus = Number(status);
return STATUS_OPTIONS.find(option => option.value === declaredStatus)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
},
{
title: '报单处理时间',
dataIndex: 'declaredDate',
valueType: 'dateTime',
renderText(time: string) {
return dayjs(Number(time)).format(TIME_FORMAT);
},
search: false,
},
{
title: '是否加企微',
dataIndex: 'weComStatus',
valueType: 'textarea',
renderText(status: number) {
return WE_COM_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={WE_COM_OPTIONS} />;
},
},
{
title: '通告所属城市',
dataIndex: 'jobCityCode',
valueType: 'textarea',
renderText(cityCode: string) {
return CITY_CODE_TO_NAME_MAP.get(cityCode);
},
renderFormItem() {
return (
<Select
showSearch
allowClear
options={CITY_OPTIONS}
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
/>
);
},
},
{
title: '报单备注信息',
dataIndex: 'declaredMark',
valueType: 'textarea',
search: false,
},
{
title: '操作',
valueType: 'option',
fixed: 'right',
width: 100,
align: 'center',
render: (_, record) => (
<a
key="config"
onClick={() => {
handleUpdateModalOpen(true);
setCurrentRow(record);
}}
>
</a>
),
},
];
return (
<PageContainer>
<ProTable<API.DeclarationListItem, API.PageParams>
headerTitle="查询表格"
actionRef={actionRef}
rowKey="key"
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
request={getDeclarationList}
columns={columns}
scroll={{ x: 'max-content' }}
/>
<ModalForm
title="更新报单信息"
width="400px"
formRef={formRef}
open={updateModalOpen}
onOpenChange={handleUpdateModalOpen}
onFinish={async data => {
const params: API.UpdateDeclarationParams = {
id: currentRow!.id,
weComStatus: data.weComStatus,
};
console.log('update confirm', data, params);
try {
await updateDeclarationInfo(params);
actionRef.current?.reload();
formRef.current?.resetFields();
} catch (e) {}
handleUpdateModalOpen(false);
}}
>
<ProFormSelect
name="weComStatus"
label="是否加企微"
options={WE_COM_OPTIONS}
rules={[{ required: true, message: '必填项' }]}
/>
<ProFormTextArea name="declaredMark" label="报单备注" placeholder="输入备注信息" />
</ModalForm>
</PageContainer>
);
};
export default TableList;

View File

@ -0,0 +1,131 @@
import type { ActionType, ProColumns, ProFormInstance } from '@ant-design/pro-components';
import { ModalForm, PageContainer, ProFormSelect, ProTable } from '@ant-design/pro-components';
import '@umijs/max';
import { Select } from 'antd';
import React, { useRef, useState } from 'react';
import { CITY_CODE_TO_NAME_MAP, CITY_OPTIONS } from '@/constants/city';
import { getGroupList, updateGroupInfo } from '@/services/list';
const STATUS_OPTIONS = [
{ label: '正常', value: 0 },
{ label: '暂停', value: 1 },
];
const TableList: React.FC = () => {
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
const [currentRow, setCurrentRow] = useState<API.GroupListItem>();
const actionRef = useRef<ActionType>();
const formRef = useRef<ProFormInstance>();
const columns: ProColumns<API.GroupListItem>[] = [
{
title: '群名称',
dataIndex: 'imGroupNick',
valueType: 'textarea',
copyable: true,
},
{
title: '群ID',
dataIndex: 'id',
valueType: 'textarea',
copyable: true,
},
{
title: '所属城市',
dataIndex: 'city',
valueType: 'textarea',
renderText(cityCode: string) {
return CITY_CODE_TO_NAME_MAP.get(cityCode);
},
renderFormItem() {
return (
<Select
showSearch
allowClear
options={CITY_OPTIONS}
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
/>
);
},
},
{
title: '群状态',
dataIndex: 'disable',
valueType: 'textarea',
renderText(disable: boolean) {
return disable ? '暂停' : '正常';
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
},
{
title: '机器人ID',
dataIndex: 'robotId',
valueType: 'textarea',
},
{
title: '机器人微信昵称',
dataIndex: 'robotImNick',
valueType: 'textarea',
},
{
title: '机器人微信账号',
dataIndex: 'robotImNo',
valueType: 'textarea',
},
{
title: '操作',
valueType: 'option',
render: (_, record) => (
<a
key="config"
onClick={() => {
handleUpdateModalOpen(true);
setCurrentRow(record);
}}
>
</a>
),
},
];
return (
<PageContainer>
<ProTable<API.GroupListItem, API.PageParams>
headerTitle="查询表格"
actionRef={actionRef}
rowKey="key"
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
request={getGroupList}
columns={columns}
/>
<ModalForm
title="更新群信息"
width="400px"
formRef={formRef}
open={updateModalOpen}
onOpenChange={handleUpdateModalOpen}
onFinish={async formData => {
const params: API.UpdateGroupParams = {
id: currentRow!.id,
city: formData.city?.value,
disable: formData.disable,
};
console.log('update confirm', formData, params);
try {
await updateGroupInfo(params);
actionRef.current?.reload();
formRef.current?.resetFields();
} catch (e) {}
handleUpdateModalOpen(false);
}}
>
<ProFormSelect.SearchSelect name="city" mode="single" label="所属城市" options={CITY_OPTIONS} />
<ProFormSelect name="disable" label="群状态" options={STATUS_OPTIONS} />
</ModalForm>
</PageContainer>
);
};
export default TableList;

View File

@ -0,0 +1,209 @@
import type { ActionType, ProColumns, ProFormInstance } from '@ant-design/pro-components';
import { ModalForm, PageContainer, ProFormSelect, ProTable } from '@ant-design/pro-components';
import '@umijs/max';
import { Select } from 'antd';
import dayjs from 'dayjs';
import React, { useRef, useState } from 'react';
import { CITY_CODE_TO_NAME_MAP, CITY_OPTIONS } from '@/constants/city';
import { TIME_FORMAT } from '@/constants/global';
import { getJobList, updateJobInfo } from '@/services/list';
const STATUS_OPTIONS = [
{ label: '正常', value: 0 },
{ label: '暂停', value: 1 },
];
const TableList: React.FC = () => {
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
const [currentRow, setCurrentRow] = useState<API.JobListItem>();
const actionRef = useRef<ActionType>();
const formRef = useRef<ProFormInstance>();
const columns: ProColumns<API.JobListItem>[] = [
{
title: '职位名称',
dataIndex: 'title',
valueType: 'textarea',
},
{
title: '职位描述',
dataIndex: 'sourceText',
valueType: 'textarea',
colSize: 2,
search: false,
copyable: true,
renderText(sourceText: string) {
return sourceText?.substring(0, 30);
},
},
{
title: '职位ID',
dataIndex: 'jobId',
valueType: 'textarea',
copyable: true,
},
{
title: '城市',
dataIndex: 'cityCode',
valueType: 'textarea',
renderText(cityCode: string) {
return CITY_CODE_TO_NAME_MAP.get(cityCode);
},
renderFormItem() {
return (
<Select
showSearch
allowClear
options={CITY_OPTIONS}
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
/>
);
},
},
{
title: '所在群名称',
dataIndex: 'imGroupNick',
valueType: 'textarea',
copyable: true,
},
{
title: '群ID',
dataIndex: 'blGroupId',
valueType: 'textarea',
search: false,
copyable: true,
},
{
title: '发布人昵称',
dataIndex: 'publisher',
valueType: 'textarea',
copyable: true,
},
{
title: '发布人ID',
dataIndex: 'blPublisherId',
valueType: 'textarea',
copyable: true,
},
{
title: '发布人微信账号',
dataIndex: 'publisherAcctNo',
valueType: 'textarea',
copyable: true,
},
{
title: '发布群数量',
dataIndex: 'relateGroupCount',
valueType: 'textarea',
search: false,
},
{
title: '机器人ID',
dataIndex: 'robotId',
valueType: 'textarea',
},
{
title: '机器人微信昵称',
dataIndex: 'robotImNick',
valueType: 'textarea',
search: false,
},
{
title: '机器人微信账号',
dataIndex: 'robotImNo',
valueType: 'textarea',
search: false,
},
{
title: '通告状态',
dataIndex: 'disable',
valueType: 'textarea',
renderText(disable: boolean) {
return disable ? '暂停' : '正常';
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
},
{
title: '小程序用户 id',
dataIndex: 'appUid',
valueType: 'textarea',
search: true,
},
{
title: '创建时间',
dataIndex: 'created',
valueType: 'dateTime',
renderText(created: string) {
return dayjs(Number(created)).format(TIME_FORMAT);
},
search: false,
},
{
title: '更新时间',
dataIndex: 'updated',
valueType: 'dateTime',
renderText(created: string) {
return dayjs(Number(created)).format(TIME_FORMAT);
},
search: false,
},
{
title: '操作',
valueType: 'option',
fixed: 'right',
width: 100,
align: 'center',
render: (_, record) => (
<a
key="config"
onClick={() => {
handleUpdateModalOpen(true);
setCurrentRow(record);
}}
>
</a>
),
},
];
return (
<PageContainer>
<ProTable<API.JobListItem, API.PageParams>
headerTitle="查询表格"
actionRef={actionRef}
rowKey="key"
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
request={getJobList}
columns={columns}
scroll={{ x: 'max-content' }}
/>
<ModalForm
title="更新通告信息"
width="400px"
formRef={formRef}
open={updateModalOpen}
onOpenChange={handleUpdateModalOpen}
onFinish={async formData => {
const params: API.UpdateJobParams = {
id: currentRow!.id,
jobId: currentRow!.jobId,
disable: Number(formData.disable) !== 0,
};
console.log('update confirm', formData, params);
try {
await updateJobInfo(params);
actionRef.current?.reload();
formRef.current?.resetFields();
} catch (e) {}
handleUpdateModalOpen(false);
}}
>
<ProFormSelect name="disable" label="通告状态" options={STATUS_OPTIONS} />
</ModalForm>
</PageContainer>
);
};
export default TableList;

View File

@ -0,0 +1,198 @@
import type { ActionType, ProColumns, ProFormInstance } from '@ant-design/pro-components';
import { ModalForm, PageContainer, ProFormSelect, ProTable } from '@ant-design/pro-components';
import '@umijs/max';
import { Select } from 'antd';
import dayjs from 'dayjs';
import React, { useRef, useState } from 'react';
import Previewer from '@/components/previewer';
import { CITY_CODE_TO_NAME_MAP, CITY_OPTIONS } from '@/constants/city';
import { TIME_FORMAT } from '@/constants/global';
import { getMaterialList, updateMaterialInfo } from '@/services/list';
const STATUS_OPTIONS = [
{ label: '开放', value: true },
{ label: '封禁', value: false },
];
const TableList: React.FC = () => {
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
const [currentRow, setCurrentRow] = useState<API.MaterialListItem>();
const actionRef = useRef<ActionType>();
const formRef = useRef<ProFormInstance>();
const columns: ProColumns<API.MaterialListItem>[] = [
{
title: '视频',
dataIndex: 'userId',
valueType: 'textarea',
copyable: true,
search: false,
render(_dom, entity) {
return <Previewer videos={entity.materialVideoInfoList} />;
},
},
{
title: '主播ID',
dataIndex: 'userId',
valueType: 'textarea',
copyable: true,
},
{
title: '模卡昵称',
dataIndex: 'name',
valueType: 'textarea',
},
{
title: '主播昵称',
dataIndex: 'nickname',
valueType: 'textarea',
search: false,
},
{
title: '自身优势',
dataIndex: 'advantages',
valueType: 'textarea',
search: false,
width: 200,
},
{
title: '模卡状态',
dataIndex: 'isOpen',
valueType: 'textarea',
renderText(status: boolean) {
return STATUS_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
search: false,
},
{
title: '主播模卡状态',
dataIndex: 'userOpen',
valueType: 'textarea',
renderText(status: boolean) {
return STATUS_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
},
{
title: '后台模卡状态',
dataIndex: 'adminOpen',
valueType: 'textarea',
renderText(status: boolean) {
return STATUS_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
},
{
title: '意向城市',
dataIndex: 'cityCode',
valueType: 'textarea',
renderText(cityCode: string) {
return CITY_CODE_TO_NAME_MAP.get(cityCode);
},
renderFormItem() {
return (
<Select
showSearch
allowClear
options={CITY_OPTIONS}
filterOption={(input, option) => (option?.label ?? '').toLowerCase().includes(input.toLowerCase())}
/>
);
},
},
{
title: '播过的品类',
dataIndex: 'workedSecCategoryStr',
valueType: 'textarea',
search: false,
width: 200,
},
{
title: '创建时间',
dataIndex: 'created',
valueType: 'dateTime',
renderText(created: string) {
return dayjs(Number(created)).format(TIME_FORMAT);
},
search: false,
sorter: true,
},
{
title: '修改时间',
dataIndex: 'updated',
valueType: 'dateTime',
renderText(created: string) {
return dayjs(Number(created)).format(TIME_FORMAT);
},
search: false,
sorter: true,
},
{
title: '操作',
valueType: 'option',
fixed: 'right',
width: 100,
align: 'center',
render: (_, record) => (
<a
key="config"
onClick={() => {
handleUpdateModalOpen(true);
setCurrentRow(record);
}}
>
</a>
),
},
];
return (
<PageContainer>
<ProTable<API.MaterialListItem, API.PageParams>
headerTitle="查询表格"
actionRef={actionRef}
rowKey="key"
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
request={getMaterialList}
columns={columns}
scroll={{ x: 'max-content' }}
/>
<ModalForm
title="更新模卡信息"
width="400px"
formRef={formRef}
open={updateModalOpen}
onOpenChange={handleUpdateModalOpen}
onFinish={async data => {
const params: API.UpdateMaterialParams = {
id: currentRow!.id,
adminOpen: Number(data.adminOpen),
};
console.log('update confirm', data, params);
try {
await updateMaterialInfo(params);
actionRef.current?.reload();
formRef.current?.resetFields();
} catch (e) {}
handleUpdateModalOpen(false);
}}
>
<ProFormSelect
name="adminOpen"
label="后台模卡状态"
options={STATUS_OPTIONS as any}
rules={[{ required: true, message: '必填项' }]}
/>
</ModalForm>
</PageContainer>
);
};
export default TableList;

View File

@ -0,0 +1,195 @@
import type { ActionType, ProColumns, ProFormInstance } from '@ant-design/pro-components';
import { ModalForm, PageContainer, ProFormSelect, ProFormText, ProTable } from '@ant-design/pro-components';
import '@umijs/max';
import { Select } from 'antd';
import dayjs from 'dayjs';
import React, { useRef, useState } from 'react';
import { TIME_FORMAT } from '@/constants/global';
import { getPublisherList, updatePublisherInfo } from '@/services/list';
const WX_STATUS_OPTIONS = [
{ label: '为空', value: 0 },
{ label: '不为空', value: 1 },
];
const STATUS_OPTIONS = [
{ label: '正常', value: 0 },
{ label: '暂停', value: 1 },
];
const ADD_WX_STATUS_OPTIONS = [
{ label: '待申请', value: 0 },
{ label: '已申请', value: 1 },
{ label: '不可添加', value: 2 },
{ label: '被封号', value: 3 },
];
const HAS_DECLARE_OPTIONS = [
{ label: '有报单', value: true },
{ label: '无报单', value: false },
];
const TableList: React.FC = () => {
const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);
const [currentRow, setCurrentRow] = useState<API.PublisherListItem>();
const actionRef = useRef<ActionType>();
const formRef = useRef<ProFormInstance>();
const columns: ProColumns<API.PublisherListItem>[] = [
{
title: '发布人昵称',
dataIndex: 'publisher',
valueType: 'textarea',
copyable: true,
},
{
title: '发布人ID',
dataIndex: 'blPublisherId',
valueType: 'textarea',
copyable: true,
},
{
title: '账号状态',
dataIndex: 'status',
valueType: 'textarea',
renderText(status: number) {
return STATUS_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={STATUS_OPTIONS} />;
},
},
{
title: '发布人微信账号',
dataIndex: 'publisherAcctNo',
valueType: 'textarea',
copyable: true,
},
{
title: '发布人微信状态',
dataIndex: 'publisherAcctStatus',
valueType: 'textarea',
renderText(status: number) {
const publisherAcctStatus = Number(status);
return WX_STATUS_OPTIONS.find(option => option.value === publisherAcctStatus)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={WX_STATUS_OPTIONS} />;
},
},
{
title: '是否申请好友',
dataIndex: 'addAcctStatus',
valueType: 'textarea',
renderText(status: number) {
return ADD_WX_STATUS_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={ADD_WX_STATUS_OPTIONS} />;
},
},
{
title: '机器人微信昵称',
dataIndex: 'robotImNick',
valueType: 'textarea',
},
{
title: '来源群',
dataIndex: 'imGroupNick',
valueType: 'textarea',
copyable: true,
},
{
title: '是否有报单',
dataIndex: 'hasDeclareOrder',
hideInTable: true,
renderText(status: boolean) {
return HAS_DECLARE_OPTIONS.find(option => option.value === status)?.label;
},
renderFormItem() {
return <Select showSearch allowClear options={HAS_DECLARE_OPTIONS} />;
},
},
{
title: '最新报单时间',
dataIndex: 'declareTime',
valueType: 'dateTime',
sorter: true,
renderText(created: string) {
return created ? dayjs(Number(created)).format(TIME_FORMAT) : '';
},
search: false,
},
{
title: '小程序用户 id',
dataIndex: 'appUid',
valueType: 'textarea',
search: true,
},
{
title: '创建时间',
dataIndex: 'created',
valueType: 'dateTime',
sorter: true,
renderText(created: string) {
return dayjs(Number(created)).format(TIME_FORMAT);
},
search: false,
},
{
title: '操作',
valueType: 'option',
render: (_, record) => (
<a
key="config"
onClick={() => {
handleUpdateModalOpen(true);
setCurrentRow(record);
}}
>
</a>
),
},
];
return (
<PageContainer>
<ProTable<API.PublisherListItem, API.PageParams>
headerTitle="查询表格"
actionRef={actionRef}
rowKey="key"
search={{ labelWidth: 120, collapsed: false, collapseRender: false }}
request={getPublisherList}
columns={columns}
/>
<ModalForm
title="更新发布人信息"
width="400px"
formRef={formRef}
open={updateModalOpen}
onOpenChange={handleUpdateModalOpen}
onFinish={async data => {
const params: API.UpdatePublisherParams = {
blPublisherId: currentRow!.blPublisherId,
publisherAcctNo: data.publisherAcctNo,
status: data.status,
addAcctStatus: data.addAcctStatus,
};
console.log('update confirm', data, params);
try {
await updatePublisherInfo(params);
actionRef.current?.reload();
formRef.current?.resetFields();
} catch (e) {}
handleUpdateModalOpen(false);
}}
>
<ProFormText width="md" name="publisherAcctNo" label="发布人微信账号" />
<ProFormSelect name="status" label="账号状态" options={STATUS_OPTIONS} />
<ProFormSelect name="addAcctStatus" label="是否申请好友" options={ADD_WX_STATUS_OPTIONS} />
</ModalForm>
</PageContainer>
);
};
export default TableList;

View File

@ -0,0 +1,104 @@
import { LockOutlined, UserOutlined } from '@ant-design/icons';
import { LoginForm, ProFormText } from '@ant-design/pro-components';
import { Helmet, history, useModel } from '@umijs/max';
import { Alert, message } from 'antd';
import { createStyles } from 'antd-style';
import React, { useState } from 'react';
import { flushSync } from 'react-dom';
import Footer from '@/components/footer';
import { login } from '@/services/user';
import { setToken } from '@/utils/login';
import Settings from '../../../../config/defaultSettings';
const useStyles = createStyles(() => {
return {
container: {
display: 'flex',
flexDirection: 'column',
height: '100vh',
overflow: 'auto',
},
};
});
const Login: React.FC = () => {
const [loginError, setLoginError] = useState<boolean>(false);
const { initialState, setInitialState } = useModel('@@initialState');
const { styles } = useStyles();
const fetchUserInfo = async () => {
const userInfo = await initialState?.fetchUserInfo?.();
if (userInfo) {
flushSync(() => {
setInitialState(s => ({
...s,
currentUser: userInfo,
}));
});
}
};
const handleSubmit = async (values: API.LoginParams) => {
try {
// 登录
console.log('login params', values);
const result = await login({ ...values });
if (result.token) {
setToken(result.token);
message.success('登录成功!');
await fetchUserInfo();
const urlParams = new URL(window.location.href).searchParams;
history.push(urlParams.get('redirect') || '/');
return;
}
console.log(result);
// 如果失败去设置用户错误信息
setLoginError(true);
} catch (error) {
console.log(error);
message.error('登录失败,请重试!');
}
};
return (
<div className={styles.container}>
<Helmet>
<title>
{'登录'}- {Settings.title}
</title>
</Helmet>
<div style={{ flex: '1', padding: '32px 0' }}>
<LoginForm
contentStyle={{
minWidth: 280,
maxWidth: '75vw',
}}
logo={<img alt="logo" src={Settings.logo} />}
subTitle=" "
title="播络管理后台"
initialValues={{
autoLogin: true,
}}
onFinish={async values => {
await handleSubmit(values as API.LoginParams);
}}
>
<ProFormText
name="userName"
fieldProps={{ size: 'large', prefix: <UserOutlined /> }}
placeholder="请输入账号"
rules={[{ required: true, message: '账号不能为空!' }]}
/>
<ProFormText.Password
name="pwd"
fieldProps={{ size: 'large', prefix: <LockOutlined /> }}
placeholder="请输入密码"
rules={[{ required: true, message: '密码不能为空!' }]}
/>
{loginError && <Alert style={{ marginBottom: 24 }} message="用户名或密码错误" type="error" showIcon />}
</LoginForm>
</div>
<Footer />
</div>
);
};
export default Login;

84
src/requestConfig.ts Normal file
View File

@ -0,0 +1,84 @@
import type { AxiosResponse, RequestOptions } from '@@/plugin-request/request';
import type { RequestConfig } from '@umijs/max';
import { message } from 'antd';
import { RESPONSE_ERROR_CODE, RESPONSE_ERROR_MESSAGE } from '@/constants/http';
import { clearToken, getToken, gotoLogin } from '@/utils/login';
import { IRequestResponse } from './types/http';
/**
* @name 全局请求配置
* @doc https://umijs.org/docs/max/request#配置
*/
export const requestConfig: RequestConfig = {
// baseURL: 'https://neighbourhood.cn',
baseURL: 'http://192.168.60.120:8082',
// 错误处理: umi@3 的错误处理方案。
errorConfig: {
// 错误抛出
errorThrower: res => {
const { code, msg, traceId } = res as IRequestResponse;
if (code) {
const error: any = new Error(msg);
error.name = 'BizError';
error.info = { code, msg, traceId };
throw error; // 抛出自制的错误
}
},
// 错误接收及处理
errorHandler: (error: any, opts: any) => {
if (opts?.skipErrorHandler) throw error;
// 我们的 errorThrower 抛出的错误。
if (error.name === 'BizError') {
const errorInfo: IRequestResponse | undefined = error.info;
if (!errorInfo) {
return;
}
const { code, msg, traceId } = errorInfo;
switch (code) {
case RESPONSE_ERROR_CODE.INVALID_PARAMETER:
default:
message.error(`Request error, msg: ${msg}, traceId: ${traceId}`);
}
} else if (error.response) {
// Axios 的错误
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
const { data, status } = error.response as AxiosResponse<IRequestResponse>;
const code = data?.code as RESPONSE_ERROR_CODE;
switch (code) {
case RESPONSE_ERROR_CODE.NEED_LOGIN:
clearToken();
gotoLogin();
return;
default:
message.error(`${RESPONSE_ERROR_MESSAGE[code] || '请求错误'}, 错误码:${status}`);
return;
}
} else if (error.request) {
// 请求已经成功发起,但没有收到响应
// \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
// 而在node.js中是 http.ClientRequest 的实例
message.error('None response! Please retry.');
} else {
// 发送请求时出了点问题
message.error('Request error, please retry.');
}
},
},
// 请求拦截器
requestInterceptors: [
(config: RequestOptions) => {
const token = getToken();
if (token) {
config.headers = {
...(config.headers || {}),
Authorization: 'Bearer ' + token,
};
}
return config;
},
],
};

59
src/service-worker.js Normal file
View File

@ -0,0 +1,59 @@
/* eslint-disable no-restricted-globals */
/* eslint-disable no-underscore-dangle */
/* globals workbox */
workbox.core.setCacheNameDetails({
prefix: 'antd-pro',
suffix: 'v5',
});
// Control all opened tabs ASAP
workbox.clientsClaim();
/**
* Use precaching list generated by workbox in build process.
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
*/
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
/**
* Register a navigation route.
* https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
*/
workbox.routing.registerNavigationRoute('/index.html');
/**
* Use runtime cache:
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute
*
* Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc.
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies
*/
/** Handle API requests */
workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst());
/** Handle third party requests */
workbox.routing.registerRoute(/^https:\/\/gw\.alipayobjects\.com\//, workbox.strategies.networkFirst());
workbox.routing.registerRoute(/^https:\/\/cdnjs\.cloudflare\.com\//, workbox.strategies.networkFirst());
workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst());
/** Response to client after skipping waiting with MessageChannel */
addEventListener('message', event => {
const replyPort = event.ports[0];
const message = event.data;
if (replyPort && message && message.type === 'skip-waiting') {
event.waitUntil(
self.skipWaiting().then(
() => {
replyPort.postMessage({
error: null,
});
},
error => {
replyPort.postMessage({
error,
});
},
),
);
}
});

224
src/services/list.ts Normal file
View File

@ -0,0 +1,224 @@
// @ts-ignore
/* eslint-disable */
import { AdminAPI } from '@/constants/api';
import { EmployType, JobType } from '@/constants/job';
import { request } from '@umijs/max';
import { SortOrder } from 'antd/es/table/interface';
function transformPageParams(params: API.PageParams & Record<string, any>) {
params.page = params.current;
delete params.current;
Object.keys(params).forEach((key: string) => {
if (typeof params[key] === 'string' && !params[key]) {
delete params[key];
}
});
return params;
}
function transformSort(sort: Record<string, SortOrder>) {
if (!sort) {
return {};
}
const sortField = Object.keys(sort)[0];
if (!sort[sortField]) {
return {};
}
const asc = sort[sortField] === 'ascend';
return { sortField, asc };
}
function sortTableList<T extends { created: number; updated: number }>(
response: API.TableList<T>,
{ sortField, asc }: ReturnType<typeof transformSort>,
): API.TableList<T> {
if (sortField === 'created' || sortField === 'updated') {
response.data.sort((itemA, itemB) => {
const valueA = Number(itemA[sortField]);
const valueB = Number(itemB[sortField]);
return asc ? valueA - valueB : valueB - valueA;
});
}
return response;
}
export async function getJobList(params: API.PageParams & Partial<API.JobListItem>, options?: {
[key: string]: any
}) {
if (!params.category) {
params.category = JobType.All;
}
if (!params.cityCode) {
params.cityCode = 'ALL';
}
if (!params.employType) {
params.employType = EmployType.All;
}
const result = await request<API.TableList<API.JobListItem>>(AdminAPI.JOB_LIST, {
method: 'POST',
data: {
...transformPageParams(params),
...(options || {}),
},
});
result.success = true;
return result;
}
/**
* 更新通告接口,必须传 数据库 id 和 jobId
*/
export async function updateJobInfo(options: API.UpdateJobParams) {
return request<API.JobListItem>(AdminAPI.JOB_UPDATE, {
method: 'POST',
data: {
...(options || {}),
},
});
}
export async function getGroupList(
params: API.PageParams & Partial<API.GroupListItem>,
options?: { [key: string]: any },
) {
const result = await request<API.TableList<API.GroupListItem>>(AdminAPI.GROUP_LIST, {
method: 'POST',
data: {
...transformPageParams(params),
...(options || {}),
},
});
result.success = true;
return result;
}
/**
* 更新群信息,必须传 数据库 id
*/
export async function updateGroupInfo(options: API.UpdateGroupParams) {
return request<API.GroupListItem>(AdminAPI.GROUP_UPDATE, {
method: 'POST',
data: {
...(options || {}),
},
});
}
export async function getAnchorGroupList(
params: API.PageParams & Partial<API.AnchorGroupListItem>,
options?: { [key: string]: any },
) {
const result = await request<API.TableList<API.AnchorGroupListItem>>(AdminAPI.ANCHOR_GROUP_LIST, {
method: 'POST',
data: {
...transformPageParams(params),
...(options || {}),
},
});
return result;
}
export async function addAnchorGroup(options?: { [key: string]: any }) {
return request<API.GroupListItem>(AdminAPI.ADD_ANCHOR_GROUP, {
method: 'POST',
data: {
...(options || {}),
},
});
}
export async function getPublisherList(
params: API.PageParams & Partial<API.PublisherListItem>,
sort: Record<string, SortOrder>,
) {
const url = params.hasDeclareOrder ? AdminAPI.PUBLISHER_DECLARATION_LIST : AdminAPI.PUBLISHER_LIST;
const result = await request<API.TableList<API.PublisherListItem>>(url, {
method: 'POST',
data: {
...transformPageParams(params),
...transformSort(sort),
},
});
return result;
}
export async function updatePublisherInfo(options: API.UpdatePublisherParams) {
return request<API.PublisherListItem>(AdminAPI.PUBLISHER_UPDATE, {
method: 'POST',
data: {
...(options || {}),
},
});
}
export async function getAnchorList(
params: API.PageParams & Partial<API.AnchorListItem>,
options?: { [key: string]: any },
) {
const result = await request<API.TableList<API.AnchorListItem>>(AdminAPI.ANCHOR_LIST, {
method: 'POST',
data: {
...transformPageParams(params),
...(options || {}),
},
});
return result;
}
export async function updateAnchorInfo(options: API.UpdateAnchorParams) {
return request<API.DeclarationListItem>(AdminAPI.ANCHOR_UPDATE, {
method: 'POST',
data: {
...(options || {}),
},
});
}
export async function getMaterialList(
params: API.PageParams & Partial<API.MaterialListItem>,
sort: Record<string, SortOrder>,
) {
const formatedSort = transformSort(sort);
const result = await request<API.TableList<API.MaterialListItem>>(AdminAPI.MATERIAL_LIST, {
method: 'POST',
data: {
...transformPageParams(params),
...formatedSort,
},
});
return sortTableList<API.MaterialListItem>(result, formatedSort);
}
export async function updateMaterialInfo(options: API.UpdateMaterialParams) {
return request<API.DeclarationListItem>(AdminAPI.MATERIAL_UPDATE, {
method: 'POST',
data: {
...(options || {}),
},
});
}
export async function getDeclarationList(
params: API.PageParams & Partial<API.DeclarationListItem>,
options?: { [key: string]: any },
) {
const result = await request<API.TableList<API.DeclarationListItem>>(AdminAPI.DECLARATION_LIST, {
method: 'POST',
data: {
...transformPageParams(params),
...(options || {}),
},
});
return result;
}
export async function updateDeclarationInfo(options: API.UpdateDeclarationParams) {
return request<API.DeclarationListItem>(AdminAPI.DECLARATION_UPDATE, {
method: 'POST',
data: {
...(options || {}),
},
});
}

325
src/services/typings.d.ts vendored Normal file
View File

@ -0,0 +1,325 @@
// @ts-ignore
/* eslint-disable */
declare namespace API {
type CurrentUser = {
id: string; // 数据库 id
userId: string;
userName: string;
created: string;
updated: string;
avatar?: string;
};
interface LoginParams {
userName?: string;
pwd?: string;
}
interface LoginResult {
token?: string;
expires?: number;
}
interface PageParams {
// proTable 给的分页页数
current?: number;
pageSize?: number;
// 实际接口需要的分页页数
page?: number;
}
interface JobListItem {
// 数据库 id
id: string;
// 小程序用户 id
appUid: string;
// 通告 id
jobId: string;
// 通告标题
title: string;
// 简介
jobDescription: string;
// 详细描述(原始信息)
sourceText: string;
// 城市 code查询时必传默认为 ALL
cityCode: string;
// 品类,查询时必传:默认为 ALL
category: string;
// 工作类型,查询时必传:默认为 ALL
employType: string;
// 发布人微信昵称
publisher: string;
// 发布人 id
blPublisherId: string;
// 发布人微信号
publisherAcctNo: string;
// 群昵称
imGroupNick: string;
// 微信群id
imGroupId: string;
// 播络群 id
blGroupId: string;
// 机器人 id
robotId: string;
// 机器人微信昵称
robotImNick: string;
// 机器人微信号
robotImNo: string;
// 通告发布群数量
relateGroupCount: number;
// 创建时间,时间戳
created: string;
// 更新时间,时间戳
updated: string;
// 是否禁用,默认为 false
disable: boolean;
}
interface GroupListItem {
// 播络群 id
id: string;
// 微信群id
imGroupId: string;
// 群昵称
imGroupNick: string;
groupType: string;
// 群主微信昵称
groupOwnerNick: string;
// 群主微信号
groupOwnerImAcctNo: string;
// 群主boluo账号
groupOwnerAcctNo: string;
// 机器人 id
robotId: string;
// 机器人微信昵称
robotImNick: string;
// 机器人微信号
robotImNo: string;
// 城市 code
city: string;
// 通告数量
jobCount: number;
// 创建时间,时间戳
created: string;
// 更新时间,时间戳
updated: string;
// 是否禁用,默认为 false
disable: boolean;
// 是否删除
isDeleted: boolean;
}
interface AnchorGroupListItem {
// 主播用户 id
userId: string;
// 主播昵称
nickName: string;
// 主播手机号
userPhone: string;
// 群名称
imGroupNick: string;
// 微信群id
imGroupId: string;
// 播络群 id
blGroupId: string;
// 是否已经支付
payed: boolean;
// 支付方式, vx、alipay、other
payType: string;
// 群定制时间,时间戳
created: string;
// 绑定群的客服操作人员
creator: string;
// 备注
mark?: string;
// 加群状态0 已申请加群 1已申请加群未进群 2 已申请加群已进群
status: number;
}
interface PublisherListItem {
// 发布人微信昵称
publisher: string;
// 发布人 id
blPublisherId: string;
// 小程序用户 id
appUid: string;
// 发布人微信号
publisherAcctNo: string;
// 发布人微信状态, 0 空1 非空
publisherAcctStatus: number;
// 账号状态: 0 正常1 暂停
status: number;
// 申请好友状态: 0 待申请1 已申请2 不能添加3 被封号
addAcctStatus: number;
phone: string;
email: string;
// 机器人微信昵称
robotImNick: string;
// 客服操作人员
operator: string;
// 群名称
imGroupNick: string;
// 微信群id
imGroupId: string;
// 播络群 id
blGroupId: string;
// 创建时间
created: string;
// 更新时间
updated: string;
// 是否有报单
hasDeclareOrder?: boolean;
// 最新报单时间
declareTime?: string;
jobId?: string;
}
interface AnchorListItem {
// 主播用户 id
userId: string;
// 主播昵称
nickName: string;
// 主播手机号
userPhone: string;
// 是否绑定了手机号
isBindPhone: boolean;
// 注册时间
created: string;
// 最后一次登录时间
lastLoginDate: string;
// 状态: 0 正常1 封禁
status: number;
// 所属城市 code
city: string;
}
interface DeclarationListItem {
// 报单 id
id: string;
// 报单类型
type: number;
// 主播用户 id
userId: string;
// 主播昵称
nickName: string;
// 主播手机号
userPhone: string;
// 通告 id
jobId: string;
// 通告标题
title: string;
// 解锁时间
useDate: string;
// 发布人微信昵称
publisher: string;
// 发布人 id
blPublisherId: string;
// 发布人微信号
publisherAcctNo: string;
// 报单时间
declarationDate: string;
// 报单处理状态
declaredStatus: number;
// 报单处理时间
declaredDate: string;
// 备注
declaredMark?: string;
// 加企微状态
weComStatus: number;
// 通告城市信息,如 400100
jobCityCode: string;
}
interface MaterialVideoInfo {
url: string;
coverUrl: string;
type: 'image' | 'video';
title: string;
isDefault: boolean;
}
interface MaterialProfile {
materialVideoInfoList: MaterialVideoInfo[];
// 基础信息
id: string;
userId: string;
name: string;
age: number;
height: number; // cm
weight: number; // kg
gender: GenderType;
shoeSize: number; // 鞋码
// 求职意向
employType: EmployType; // 工作类型
fullTimeMinPrice: number; // 全职期望薪资下限
fullTimeMaxPrice: number; // 全职期望薪资上限
partyTimeMinPrice: number; // 兼职期望薪资下限
partyTimeMaxPrice: number; // 兼职期望薪资上限
cityCode: string; // 城市
cityCodes: string; // 城市。多个城市用 、分割,如 '110100、141100'
acceptWorkForSit: boolean; // 是否接受坐班
// 直播经验
workedYear: WorkedYears; // 工作年限单位年无为0、半年 0.5、1、2、3、4、5年及以上用100表示默认为1年
workedAccounts: string; // 直播过的账号
newAccountExperience: number; // 是否有起号经验0无 1有
workedCategory: string; // 直播过的品类
workedSecCategoryStr: string; // 直播过的二级品类
style: string; // 风格。多个分割用 、分割
maxGmv: number; // 最高 GMV单位 w
maxOnline: number; // 最高在线人数
// 自身优势
advantages: string;
// 其他
approveStatus: boolean; // 审核状态0 待审 1 成功 2 不通过
isOpen: boolean; // 整体状态是否开放 1开放 0不开放
createType: ProfileCreateSource;
creator: string; // 创建人id
progressBar: number; // 进度百分比
filledItems: number; // 已填资料项数
created: number; // 时间戳
updated: number; // 时间戳
}
interface MaterialListItem extends MaterialProfile {
// 主播用户 id
userId: string;
// 主播昵称
nickname: string;
// 主播手机号
userPhone: string;
userOpen: boolean;
adminOpen: boolean; // 后台控制是否开放 1开放 0不开放
}
interface ListResult<T> {
data: T[];
total: number;
hasMore: boolean;
page: number;
pageSize: number;
// 排序字段
sortField: string;
// 是否升序
asc: string;
}
interface TableList<T> extends ListResult<T> {
success?: boolean;
}
type UpdateJobParams = Pick<JobListItem, 'id' | 'jobId' | 'disable'>;
type UpdateGroupParams = Pick<GroupListItem, 'id' | 'city' | 'disable'>;
type UpdatePublisherParams = Pick<
PublisherListItem,
'blPublisherId' | 'publisherAcctNo' | 'status' | 'addAcctStatus'
>;
type UpdateAnchorParams = Pick<AnchorListItem, 'userId' | 'status'>;
type UpdateDeclarationParams = Pick<DeclarationListItem, 'id' | 'weComStatus'>;
type UpdateMaterialParams = Pick<MaterialListItem, 'id'> & { adminOpen: number };
}

31
src/services/user.ts Normal file
View File

@ -0,0 +1,31 @@
// @ts-ignore
/* eslint-disable */
import { AdminAPI } from '@/constants/api';
import { request } from '@umijs/max';
import { md5 } from 'js-md5';
export async function currentUser(options?: { [key: string]: any }) {
return request<API.CurrentUser>(AdminAPI.USER, {
method: 'POST',
...(options || {}),
});
}
export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
body.pwd = md5(body.pwd || '');
return request<API.LoginResult>(AdminAPI.LOGIN, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
export async function outLogin(options?: { [key: string]: any }) {
// return request<Record<string, any>>(AdminAPI.OUT_LOGIN, {
// method: 'POST',
// ...(options || {}),
// });
}

9
src/types/http.ts Normal file
View File

@ -0,0 +1,9 @@
import { RESPONSE_ERROR_CODE } from '@/constants/http';
export interface IRequestResponse<T = any> {
data: T;
// 请求出错时才会返回下面几个字段
code: RESPONSE_ERROR_CODE;
msg: string;
traceId: string;
}

20
src/typings.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
declare module 'slash2';
declare module '*.css';
declare module '*.less';
declare module '*.scss';
declare module '*.sass';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';
declare module 'omit.js';
declare module 'numeral';
declare module '@antv/data-set';
declare module 'mockjs';
declare module 'react-fittext';
declare module 'bizcharts-plugin-slider';
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;

6
src/utils/common.ts Normal file
View File

@ -0,0 +1,6 @@
export const isValidPhone = (phone: string, callback: (msg?: string) => void) => {
if (!/^1\d{10}$/.test(phone)) {
callback('手机号格式不正确');
}
callback();
};

22
src/utils/login.ts Normal file
View File

@ -0,0 +1,22 @@
import { history } from '@umijs/max';
import { LOGIN_PATH } from '@/constants/global';
let _token = '';
export const setToken = (token: string) => {
_token = token;
};
export const getToken = () => {
return _token;
};
export const clearToken = () => {
return (_token = '');
};
export const gotoLogin = () => {
console.trace('gotoLogin');
history.push(LOGIN_PATH);
};

64
tests/setupTests.jsx Normal file
View File

@ -0,0 +1,64 @@
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
removeItem: jest.fn(),
clear: jest.fn(),
};
global.localStorage = localStorageMock;
Object.defineProperty(URL, 'createObjectURL', {
writable: true,
value: jest.fn(),
});
class Worker {
constructor(stringUrl) {
this.url = stringUrl;
this.onmessage = () => {};
}
postMessage(msg) {
this.onmessage(msg);
}
}
window.Worker = Worker;
/* eslint-disable global-require */
if (typeof window !== 'undefined') {
// ref: https://github.com/ant-design/ant-design/issues/18774
if (!window.matchMedia) {
Object.defineProperty(global.window, 'matchMedia', {
writable: true,
configurable: true,
value: jest.fn(() => ({
matches: false,
addListener: jest.fn(),
removeListener: jest.fn(),
})),
});
}
if (!window.matchMedia) {
Object.defineProperty(global.window, 'matchMedia', {
writable: true,
configurable: true,
value: jest.fn(query => ({
matches: query.includes('max-width'),
addListener: jest.fn(),
removeListener: jest.fn(),
})),
});
}
}
const errorLog = console.error;
Object.defineProperty(global.window.console, 'error', {
writable: true,
configurable: true,
value: (...rest) => {
const logStr = rest.join('');
if (logStr.includes('Warning: An update to %s inside a test was not wrapped in act(...)')) {
return;
}
errorLog(...rest);
},
});

23
tsconfig.json Normal file
View File

@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"jsx": "preserve",
"esModuleInterop": true,
"sourceMap": true,
"baseUrl": "./",
"skipLibCheck": true,
"experimentalDecorators": true,
"strict": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"paths": {
"@/*": ["./src/*"],
"@@/*": ["./src/.umi/*"],
"@@test/*": ["./src/.umi-test/*"]
}
},
"include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"]
}

BIN
未命名文件夹/dist.zip Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
"use strict";(self.webpackChunkboluo_admin=self.webpackChunkboluo_admin||[]).push([[849],{18037:function(s,n,t){t.r(n),t.d(n,{default:function(){return E}});var O=t(75271),u=t(67929),o=t(52676);function E(){var l=(0,u.useOutletContext)();return(0,o.jsx)(u.Outlet,{context:l})}}}]);

0
未命名文件夹/dist/CNAME vendored Normal file
View File

BIN
未命名文件夹/dist/favicon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

16
未命名文件夹/dist/index.html vendored Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>播络管理后台</title>
<link rel="stylesheet" href="/umi.fe20e75b.css">
<script async src="/scripts/loading.js"></script>
<script src="/preload_helper.156ed083.js"></script>
</head>
<body>
<div id="root"></div>
<script src="/umi.6921976f.js"></script>
</body>
</html>

40
未命名文件夹/dist/logo.svg vendored Normal file
View File

@ -0,0 +1,40 @@
<svg width="119" height="150" viewBox="0 0 119 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M80.175 13.0632C77.7363 14.5724 72.8825 18.5651 70.9547 22.1217C72.101 23.5385 74.2688 26.4282 74.9477 29.151L74.9327 29.1973C74.94 29.1911 74.9473 29.1849 74.9546 29.1787C74.9569 29.188 74.9592 29.1973 74.9614 29.2066L74.9765 29.1603C77.1261 27.3566 80.5784 26.293 82.3386 25.8206C82.8695 21.8101 81.2895 15.7269 80.2037 13.0725L80.2147 13.0387C80.2081 13.0427 80.2015 13.0468 80.1948 13.0509C80.1919 13.0437 80.1889 13.0365 80.186 13.0293L80.175 13.0632Z" fill="url(#paint0_linear_1133_1248)"/>
<rect x="30.068" y="34.5977" width="81" height="95" rx="40" transform="rotate(18 30.068 34.5977)" fill="url(#paint1_linear_1133_1248)"/>
<mask id="mask0_1133_1248" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="11" y="44" width="86" height="96">
<rect x="30.068" y="34.5977" width="81" height="95" rx="40" transform="rotate(18 30.068 34.5977)" fill="#6D3DF5"/>
</mask>
<g mask="url(#mask0_1133_1248)">
<path d="M44.1998 86.6703C37.3495 101.333 53.9573 129.851 56.966 141.123C50.6532 140.738 33.4868 137.658 19.2811 129.03C7.9165 122.127 3.17011 99.692 5.06788 89.3529C7.03047 80.549 14.0224 65.124 32.3405 57.7505C50.6585 50.377 77.9175 62.5192 82.781 63.2914C78.1227 67.8123 51.0501 72.0077 44.1998 86.6703Z" fill="url(#paint2_linear_1133_1248)"/>
<path d="M65.2721 89.672C56.6928 75.9492 31.4296 68.1613 20.334 64.5561C24.0902 59.4678 28.3099 52.9529 43.2763 45.724C55.2494 39.9408 76.6578 48.1585 84.3022 55.3739C90.6231 61.8088 99.7651 76.065 95.991 95.4473C92.2168 114.83 82.3105 126.226 79.0175 129.888L78.9682 129.639C77.657 123.01 73.7425 103.221 65.2721 89.672Z" fill="url(#paint3_linear_1133_1248)"/>
<path d="M54.3243 105.567C72.0134 102.903 82.3998 85.5969 94.1322 68.9053C96.6697 74.6985 99.4948 87.9969 94.0752 103.059C89.5736 115.571 85.9242 123.194 79.3388 130.518C71.3267 137.378 71.2493 137.241 62.0994 139.986C46.9544 144.528 41.9848 104.712 41.8182 103.606C45.9435 105.472 48.3502 106.467 54.3243 105.567Z" fill="url(#paint4_linear_1133_1248)"/>
</g>
<path d="M49.346 78.2787C50.1089 77.5699 51.1851 77.463 52.1429 77.8716C54.5231 78.8868 59.2767 81.3391 63.2464 86.0225C67.068 90.5312 68.8716 94.6597 69.6235 96.7902C69.9625 97.7509 69.8012 98.7972 69.1237 99.558C67.5202 101.359 63.7786 104.576 56.8886 105.614C50.361 106.597 45.798 105.796 43.4931 105.157C42.3728 104.847 41.5843 103.924 41.4146 102.774C41.005 99.9996 40.6797 94.3251 43.2682 88.0447C45.5974 82.3935 47.948 79.5776 49.346 78.2787Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M72.6959 36.1793C73.3511 22.5129 57.676 16.5076 50.0756 15.0896C59.4485 23.5722 57.0116 35.3237 54.4321 40.6567C59.4124 40.307 64.5418 40.8902 69.5817 42.5277L70.5328 42.8367C75.6599 44.5026 80.2205 47.1058 84.0867 50.395L84.8227 50.6341C85.8327 44.8288 90.7533 33.7742 103.392 32.4133C96.41 29.0931 80.1988 24.7378 72.6959 36.1793Z" fill="url(#paint5_linear_1133_1248)"/>
<defs>
<linearGradient id="paint0_linear_1133_1248" x1="79.7104" y1="12.8748" x2="74.4572" y2="29.0428" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFA564"/>
<stop offset="1" stop-color="#FF8064"/>
</linearGradient>
<linearGradient id="paint1_linear_1133_1248" x1="100.568" y1="73.5977" x2="83.9796" y2="125.245" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D3DF5"/>
<stop offset="1" stop-color="#3D65F5"/>
</linearGradient>
<linearGradient id="paint2_linear_1133_1248" x1="43.6429" y1="140.834" x2="67.3536" y2="51.3927" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D3DF5"/>
<stop offset="1" stop-color="#5A87FC"/>
</linearGradient>
<linearGradient id="paint3_linear_1133_1248" x1="25.9803" y1="58.5047" x2="78.4685" y2="99.217" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D3DF5"/>
<stop offset="1" stop-color="#5A87FC"/>
</linearGradient>
<linearGradient id="paint4_linear_1133_1248" x1="105.072" y1="87.7149" x2="38.3481" y2="87.1272" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D3DF5"/>
<stop offset="1" stop-color="#5A87FC"/>
</linearGradient>
<linearGradient id="paint5_linear_1133_1248" x1="58.4025" y1="17.4824" x2="51.1823" y2="39.7037" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFA564"/>
<stop offset="1" stop-color="#FF8064"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1 @@
"use strict";(self.webpackChunkboluo_admin=self.webpackChunkboluo_admin||[]).push([[571],{76682:function(s,t,n){n.r(t);var a=n(67929),E=n(82436),_=n(97705),P=n(75271),u=n(52676),o=function(){return(0,u.jsx)(E.ZP,{status:"404",title:"404",subTitle:"\u62B1\u6B49\uFF0C\u60A8\u8BBF\u95EE\u7684\u9875\u9762\u4E0D\u5B58\u5728\u3002",extra:(0,u.jsx)(_.ZP,{type:"primary",onClick:function(){return a.history.push("/")},children:"\u8FD4\u56DE\u9996\u9875"})})};t.default=o}}]);

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
"use strict";(self.webpackChunkboluo_admin=self.webpackChunkboluo_admin||[]).push([[880],{90620:function(N,m,e){e.r(m);var R=e(90228),E=e.n(R),b=e(87999),h=e.n(b),C=e(48305),c=e.n(C),U=e(93218),x=e(19191),A=e(90103),L=e(49973),Z=e(67929),v=e(4481),B=e(16483),T=e.n(B),s=e(75271),f=e(11039),p=e(55603),O=e(29078),l=e(52676),u=[{label:"\u6B63\u5E38",value:0},{label:"\u5C01\u7981",value:1}],y=function(){var g=(0,s.useState)(!1),D=c()(g,2),K=D[0],d=D[1],W=(0,s.useState)(),M=c()(W,2),j=M[0],S=M[1],P=(0,s.useRef)(),I=(0,s.useRef)(),F=[{title:"\u4E3B\u64AD\u6635\u79F0",dataIndex:"nickName",valueType:"textarea"},{title:"\u4E3B\u64ADID",dataIndex:"userId",valueType:"textarea",copyable:!0},{title:"\u4E3B\u64AD\u624B\u673A\u53F7",dataIndex:"userPhone",valueType:"textarea",copyable:!0},{title:"\u6CE8\u518C\u65F6\u95F4",dataIndex:"created",valueType:"dateTime",renderText:function(n){return T()(Number(n)).format(p.T)},search:!1},{title:"\u6700\u540E\u767B\u5F55\u65F6\u95F4",dataIndex:"lastLoginDate",valueType:"textarea",search:!1,renderText:function(n){return T()(Number(n)).format(p.T)}},{title:"\u8D26\u53F7\u72B6\u6001",dataIndex:"status",valueType:"textarea",renderText:function(n){var _;return(_=u.find(function(a){return a.value===n}))===null||_===void 0?void 0:_.label},renderFormItem:function(){return(0,l.jsx)(v.Z,{showSearch:!0,allowClear:!0,options:u})}},{title:"\u57CE\u5E02",dataIndex:"city",valueType:"textarea",renderText:function(n){return f.JO.get(n)},renderFormItem:function(){return(0,l.jsx)(v.Z,{showSearch:!0,allowClear:!0,options:f.ZD,filterOption:function(_,a){var o;return((o=a==null?void 0:a.label)!==null&&o!==void 0?o:"").toLowerCase().includes(_.toLowerCase())}})}},{title:"\u64CD\u4F5C",valueType:"option",render:function(n,_){return(0,l.jsx)("a",{onClick:function(){d(!0),S(_)},children:"\u4FEE\u6539"},"config")}}];return(0,l.jsxs)(U._z,{children:[(0,l.jsx)(x.Z,{headerTitle:"\u67E5\u8BE2\u8868\u683C",actionRef:P,rowKey:"key",search:{labelWidth:120,collapsed:!1,collapseRender:!1},request:O.q4,columns:F}),(0,l.jsx)(A.Y,{title:"\u66F4\u65B0\u4E3B\u64AD\u4FE1\u606F",width:"400px",formRef:I,open:K,onOpenChange:d,onFinish:function(){var r=h()(E()().mark(function n(_){var a,o,i;return E()().wrap(function(t){for(;;)switch(t.prev=t.next){case 0:return a={userId:j.userId,status:_.status},console.log("update confirm",_,a),t.prev=2,t.next=5,(0,O.FM)(a);case 5:(o=P.current)===null||o===void 0||o.reload(),(i=I.current)===null||i===void 0||i.resetFields(),t.next=11;break;case 9:t.prev=9,t.t0=t.catch(2);case 11:d(!1);case 12:case"end":return t.stop()}},n,null,[[2,9]])}));return function(n){return r.apply(this,arguments)}}(),children:(0,l.jsx)(L.Z,{name:"status",label:"\u8D26\u53F7\u72B6\u6001",options:u,rules:[{required:!0,message:"\u5FC5\u586B\u9879"}]})})]})};m.default=y}}]);

View File

@ -0,0 +1 @@
"use strict";(self.webpackChunkboluo_admin=self.webpackChunkboluo_admin||[]).push([[544],{71478:function(k,S,e){e.r(S),e.d(S,{default:function(){return Q}});var g=e(90228),y=e.n(g),N=e(87999),Z=e.n(N),D=e(48305),F=e.n(D),$=e(93218),M=e(19191),L=e(90103),W=e(49973),U=e(36161),Y=e(11267),i=e(75271),B=e(17146),u=e(52676),G=["fieldProps","proFieldProps"],w=function(s,c){var p=s.fieldProps,v=s.proFieldProps,h=(0,Y.Z)(s,G);return(0,u.jsx)(B.Z,(0,U.Z)({ref:c,valueType:"textarea",fieldProps:p,proFieldProps:v},h))},z=i.forwardRef(w),q=e(67929),f=e(4481),J=e(16483),x=e.n(J),C=e(11039),m=e(55603),_=function(n){return n.Free="free",n.VX="vx",n.AliPay="alipay",n.Other="other",n}({}),O=function(n){return n[n.Direct=0]="Direct",n[n.CS=1]="CS",n}({}),P=e(29078),b=[{label:"\u5F85\u5904\u7406",value:0},{label:"\u5DF2\u5904\u7406",value:1}],j=[{label:"\u76F4\u63A5\u8054\u7CFB",value:O.Direct},{label:"\u5BA2\u670D\u8054\u7CFB",value:O.CS}],T=[{label:"\u672A\u52A0",value:0},{label:"\u5DF2\u52A0",value:1}],K=function(){var s=(0,i.useState)(!1),c=F()(s,2),p=c[0],v=c[1],h=(0,i.useState)(),R=F()(h,2),V=R[0],X=R[1],A=(0,i.useRef)(),E=(0,i.useRef)(),H=[{title:"\u4E3B\u64AD\u6635\u79F0",dataIndex:"nickName",valueType:"textarea"},{title:"\u4E3B\u64ADID",dataIndex:"userId",valueType:"textarea",copyable:!0},{title:"\u4E3B\u64AD\u624B\u673A\u53F7",dataIndex:"userPhone",valueType:"textarea",copyable:!0},{title:"\u901A\u544A\u6807\u9898",dataIndex:"title",valueType:"textarea"},{title:"\u901A\u544AID",dataIndex:"jobId",valueType:"textarea",copyable:!0},{title:"\u89E3\u9501\u65F6\u95F4",dataIndex:"useDate",valueType:"dateTime",renderText:function(t){return x()(Number(t)).format(m.T)},search:!1},{title:"\u804C\u4F4D\u53D1\u5E03\u4EBA\u6635\u79F0",dataIndex:"publisher",valueType:"textarea",search:!1,copyable:!0},{title:"\u804C\u4F4D\u53D1\u5E03\u4EBAID",dataIndex:"blPublisherId",valueType:"textarea",copyable:!0},{title:"\u804C\u4F4D\u53D1\u5E03\u4EBA\u5FAE\u4FE1",dataIndex:"publisherAcctNo",valueType:"textarea",copyable:!0},{title:"\u62A5\u5355\u7C7B\u578B",dataIndex:"type",valueType:"textarea",renderText:function(t){var r;return(r=j.find(function(a){return a.value===t}))===null||r===void 0?void 0:r.label},renderFormItem:function(){return(0,u.jsx)(f.Z,{allowClear:!0,options:j})}},{title:"\u62A5\u5355\u65F6\u95F4",dataIndex:"declarationDate",valueType:"dateTime",renderText:function(t){return x()(Number(t)).format(m.T)},search:!1},{title:"\u62A5\u5355\u5904\u7406\u72B6\u6001",dataIndex:"declaredStatus",valueType:"textarea",renderText:function(t){var r,a=Number(t);return(r=b.find(function(o){return o.value===a}))===null||r===void 0?void 0:r.label},renderFormItem:function(){return(0,u.jsx)(f.Z,{showSearch:!0,allowClear:!0,options:b})}},{title:"\u62A5\u5355\u5904\u7406\u65F6\u95F4",dataIndex:"declaredDate",valueType:"dateTime",renderText:function(t){return x()(Number(t)).format(m.T)},search:!1},{title:"\u662F\u5426\u52A0\u4F01\u5FAE",dataIndex:"weComStatus",valueType:"textarea",renderText:function(t){var r;return(r=T.find(function(a){return a.value===t}))===null||r===void 0?void 0:r.label},renderFormItem:function(){return(0,u.jsx)(f.Z,{showSearch:!0,allowClear:!0,options:T})}},{title:"\u901A\u544A\u6240\u5C5E\u57CE\u5E02",dataIndex:"jobCityCode",valueType:"textarea",renderText:function(t){return C.JO.get(t)},renderFormItem:function(){return(0,u.jsx)(f.Z,{showSearch:!0,allowClear:!0,options:C.ZD,filterOption:function(r,a){var o;return((o=a==null?void 0:a.label)!==null&&o!==void 0?o:"").toLowerCase().includes(r.toLowerCase())}})}},{title:"\u62A5\u5355\u5907\u6CE8\u4FE1\u606F",dataIndex:"declaredMark",valueType:"textarea",search:!1},{title:"\u64CD\u4F5C",valueType:"option",fixed:"right",width:100,align:"center",render:function(t,r){return(0,u.jsx)("a",{onClick:function(){v(!0),X(r)},children:"\u4FEE\u6539"},"config")}}];return(0,u.jsxs)($._z,{children:[(0,u.jsx)(M.Z,{headerTitle:"\u67E5\u8BE2\u8868\u683C",actionRef:A,rowKey:"key",search:{labelWidth:120,collapsed:!1,collapseRender:!1},request:P.Dn,columns:H,scroll:{x:"max-content"}}),(0,u.jsxs)(L.Y,{title:"\u66F4\u65B0\u62A5\u5355\u4FE1\u606F",width:"400px",formRef:E,open:p,onOpenChange:v,onFinish:function(){var l=Z()(y()().mark(function t(r){var a,o,I;return y()().wrap(function(d){for(;;)switch(d.prev=d.next){case 0:return a={id:V.id,weComStatus:r.weComStatus},console.log("update confirm",r,a),d.prev=2,d.next=5,(0,P.$Q)(a);case 5:(o=A.current)===null||o===void 0||o.reload(),(I=E.current)===null||I===void 0||I.resetFields(),d.next=11;break;case 9:d.prev=9,d.t0=d.catch(2);case 11:v(!1);case 12:case"end":return d.stop()}},t,null,[[2,9]])}));return function(t){return l.apply(this,arguments)}}(),children:[(0,u.jsx)(W.Z,{name:"weComStatus",label:"\u662F\u5426\u52A0\u4F01\u5FAE",options:T,rules:[{required:!0,message:"\u5FC5\u586B\u9879"}]}),(0,u.jsx)(z,{name:"declaredMark",label:"\u62A5\u5355\u5907\u6CE8",placeholder:"\u8F93\u5165\u5907\u6CE8\u4FE1\u606F"})]})]})},Q=K}}]);

View File

@ -0,0 +1 @@
"use strict";(self.webpackChunkboluo_admin=self.webpackChunkboluo_admin||[]).push([[583],{85687:function(F,c,e){e.r(c);var P=e(90228),E=e.n(P),h=e(87999),C=e.n(h),R=e(48305),p=e.n(R),x=e(93218),U=e(19191),y=e(90103),v=e(49973),S=e(67929),O=e(4481),s=e(75271),u=e(11039),f=e(29078),_=e(52676),T=[{label:"\u6B63\u5E38",value:0},{label:"\u6682\u505C",value:1}],A=function(){var L=(0,s.useState)(!1),b=p()(L,2),B=b[0],d=b[1],g=(0,s.useState)(),D=p()(g,2),K=D[0],W=D[1],I=(0,s.useRef)(),M=(0,s.useRef)(),j=[{title:"\u7FA4\u540D\u79F0",dataIndex:"imGroupNick",valueType:"textarea",copyable:!0},{title:"\u7FA4ID",dataIndex:"id",valueType:"textarea",copyable:!0},{title:"\u6240\u5C5E\u57CE\u5E02",dataIndex:"city",valueType:"textarea",renderText:function(a){return u.JO.get(a)},renderFormItem:function(){return(0,_.jsx)(O.Z,{showSearch:!0,allowClear:!0,options:u.ZD,filterOption:function(r,t){var o;return((o=t==null?void 0:t.label)!==null&&o!==void 0?o:"").toLowerCase().includes(r.toLowerCase())}})}},{title:"\u7FA4\u72B6\u6001",dataIndex:"disable",valueType:"textarea",renderText:function(a){return a?"\u6682\u505C":"\u6B63\u5E38"},renderFormItem:function(){return(0,_.jsx)(O.Z,{showSearch:!0,allowClear:!0,options:T})}},{title:"\u673A\u5668\u4EBAID",dataIndex:"robotId",valueType:"textarea"},{title:"\u673A\u5668\u4EBA\u5FAE\u4FE1\u6635\u79F0",dataIndex:"robotImNick",valueType:"textarea"},{title:"\u673A\u5668\u4EBA\u5FAE\u4FE1\u8D26\u53F7",dataIndex:"robotImNo",valueType:"textarea"},{title:"\u64CD\u4F5C",valueType:"option",render:function(a,r){return(0,_.jsx)("a",{onClick:function(){d(!0),W(r)},children:"\u4FEE\u6539"},"config")}}];return(0,_.jsxs)(x._z,{children:[(0,_.jsx)(U.Z,{headerTitle:"\u67E5\u8BE2\u8868\u683C",actionRef:I,rowKey:"key",search:{labelWidth:120,collapsed:!1,collapseRender:!1},request:f.hI,columns:j}),(0,_.jsxs)(y.Y,{title:"\u66F4\u65B0\u7FA4\u4FE1\u606F",width:"400px",formRef:M,open:B,onOpenChange:d,onFinish:function(){var l=C()(E()().mark(function a(r){var t,o,i,m;return E()().wrap(function(n){for(;;)switch(n.prev=n.next){case 0:return o={id:K.id,city:(t=r.city)===null||t===void 0?void 0:t.value,disable:r.disable},console.log("update confirm",r,o),n.prev=2,n.next=5,(0,f.tF)(o);case 5:(i=I.current)===null||i===void 0||i.reload(),(m=M.current)===null||m===void 0||m.resetFields(),n.next=11;break;case 9:n.prev=9,n.t0=n.catch(2);case 11:d(!1);case 12:case"end":return n.stop()}},a,null,[[2,9]])}));return function(a){return l.apply(this,arguments)}}(),children:[(0,_.jsx)(v.Z.SearchSelect,{name:"city",mode:"single",label:"\u6240\u5C5E\u57CE\u5E02",options:u.ZD}),(0,_.jsx)(v.Z,{name:"disable",label:"\u7FA4\u72B6\u6001",options:T})]})]})};c.default=A}}]);

View File

@ -0,0 +1 @@
"use strict";(self.webpackChunkboluo_admin=self.webpackChunkboluo_admin||[]).push([[209],{7690:function(N,i,e){e.r(i);var M=e(90228),m=e.n(M),P=e(87999),y=e.n(P),C=e(48305),c=e.n(C),R=e(93218),U=e(19191),A=e(90103),L=e(49973),G=e(67929),p=e(4481),B=e(16483),E=e.n(B),d=e(75271),v=e(11039),T=e(55603),I=e(29078),_=e(52676),f=[{label:"\u6B63\u5E38",value:0},{label:"\u6682\u505C",value:1}],g=function(){var j=(0,d.useState)(!1),b=c()(j,2),K=b[0],u=b[1],W=(0,d.useState)(),x=c()(W,2),O=x[0],S=x[1],D=(0,d.useRef)(),h=(0,d.useRef)(),F=[{title:"\u804C\u4F4D\u540D\u79F0",dataIndex:"title",valueType:"textarea"},{title:"\u804C\u4F4D\u63CF\u8FF0",dataIndex:"sourceText",valueType:"textarea",colSize:2,search:!1,copyable:!0,renderText:function(a){return a==null?void 0:a.substring(0,30)}},{title:"\u804C\u4F4DID",dataIndex:"jobId",valueType:"textarea",copyable:!0},{title:"\u57CE\u5E02",dataIndex:"cityCode",valueType:"textarea",renderText:function(a){return v.JO.get(a)},renderFormItem:function(){return(0,_.jsx)(p.Z,{showSearch:!0,allowClear:!0,options:v.ZD,filterOption:function(o,r){var l;return((l=r==null?void 0:r.label)!==null&&l!==void 0?l:"").toLowerCase().includes(o.toLowerCase())}})}},{title:"\u6240\u5728\u7FA4\u540D\u79F0",dataIndex:"imGroupNick",valueType:"textarea",copyable:!0},{title:"\u7FA4ID",dataIndex:"blGroupId",valueType:"textarea",search:!1,copyable:!0},{title:"\u53D1\u5E03\u4EBA\u6635\u79F0",dataIndex:"publisher",valueType:"textarea",copyable:!0},{title:"\u53D1\u5E03\u4EBAID",dataIndex:"blPublisherId",valueType:"textarea",copyable:!0},{title:"\u53D1\u5E03\u4EBA\u5FAE\u4FE1\u8D26\u53F7",dataIndex:"publisherAcctNo",valueType:"textarea",copyable:!0},{title:"\u53D1\u5E03\u7FA4\u6570\u91CF",dataIndex:"relateGroupCount",valueType:"textarea",search:!1},{title:"\u673A\u5668\u4EBAID",dataIndex:"robotId",valueType:"textarea"},{title:"\u673A\u5668\u4EBA\u5FAE\u4FE1\u6635\u79F0",dataIndex:"robotImNick",valueType:"textarea",search:!1},{title:"\u673A\u5668\u4EBA\u5FAE\u4FE1\u8D26\u53F7",dataIndex:"robotImNo",valueType:"textarea",search:!1},{title:"\u901A\u544A\u72B6\u6001",dataIndex:"disable",valueType:"textarea",renderText:function(a){return a?"\u6682\u505C":"\u6B63\u5E38"},renderFormItem:function(){return(0,_.jsx)(p.Z,{showSearch:!0,allowClear:!0,options:f})}},{title:"\u5C0F\u7A0B\u5E8F\u7528\u6237 id",dataIndex:"appUid",valueType:"textarea",search:!0},{title:"\u521B\u5EFA\u65F6\u95F4",dataIndex:"created",valueType:"dateTime",renderText:function(a){return E()(Number(a)).format(T.T)},search:!1},{title:"\u66F4\u65B0\u65F6\u95F4",dataIndex:"updated",valueType:"dateTime",renderText:function(a){return E()(Number(a)).format(T.T)},search:!1},{title:"\u64CD\u4F5C",valueType:"option",fixed:"right",width:100,align:"center",render:function(a,o){return(0,_.jsx)("a",{onClick:function(){u(!0),S(o)},children:"\u4FEE\u6539"},"config")}}];return(0,_.jsxs)(R._z,{children:[(0,_.jsx)(U.Z,{headerTitle:"\u67E5\u8BE2\u8868\u683C",actionRef:D,rowKey:"key",search:{labelWidth:120,collapsed:!1,collapseRender:!1},request:I.Go,columns:F,scroll:{x:"max-content"}}),(0,_.jsx)(A.Y,{title:"\u66F4\u65B0\u901A\u544A\u4FE1\u606F",width:"400px",formRef:h,open:K,onOpenChange:u,onFinish:function(){var t=y()(m()().mark(function a(o){var r,l,s;return m()().wrap(function(n){for(;;)switch(n.prev=n.next){case 0:return r={id:O.id,jobId:O.jobId,disable:Number(o.disable)!==0},console.log("update confirm",o,r),n.prev=2,n.next=5,(0,I.$y)(r);case 5:(l=D.current)===null||l===void 0||l.reload(),(s=h.current)===null||s===void 0||s.resetFields(),n.next=11;break;case 9:n.prev=9,n.t0=n.catch(2);case 11:u(!1);case 12:case"end":return n.stop()}},a,null,[[2,9]])}));return function(a){return t.apply(this,arguments)}}(),children:(0,_.jsx)(L.Z,{name:"disable",label:"\u901A\u544A\u72B6\u6001",options:f})})]})};i.default=g}}]);

View File

@ -0,0 +1 @@
"use strict";(self.webpackChunkboluo_admin=self.webpackChunkboluo_admin||[]).push([[935],{66268:function(Y,g,e){e.r(g),e.d(g,{default:function(){return J}});var N=e(90228),O=e.n(N),Z=e(87999),A=e.n(Z),$=e(48305),C=e.n($),L=e(93218),U=e(19191),E=e(90103),B=e(49973),H=e(67929),c=e(4481),M=e(16483),R=e.n(M),m=e(75271),j=e(45973),b=e(13033),a=e(52676),D=(0,b.kc)(function(){return{container:{width:330,display:"flex",flexDirection:"row",flexWrap:"wrap",marginLeft:"-10px",marginBottom:"-10px"},imageContainer:{marginLeft:"10px",marginBottom:"10px"}}}),G=function(y){var v=y.videos,x=v===void 0?[]:v,f=D(),T=f.styles,p=function(I,h){var s=x[h.current];return!s||s.type==="image"?I:(0,a.jsx)("video",{controls:!0,autoPlay:!0,width:"400px",src:s.url})};return(0,a.jsx)("div",{className:T.container,children:(0,a.jsx)(j.Z.PreviewGroup,{preview:{imageRender:p,toolbarRender:function(){return null},destroyOnClose:!0},children:x.map(function(d){return(0,a.jsx)(j.Z,{width:100,src:d.coverUrl,wrapperClassName:T.imageContainer},d.coverUrl)})})})},W=G,F=e(11039),w=e(55603),P=e(29078),o=[{label:"\u5F00\u653E",value:!0},{label:"\u5C01\u7981",value:!1}],z=function(){var y=(0,m.useState)(!1),v=C()(y,2),x=v[0],f=v[1],T=(0,m.useState)(),p=C()(T,2),d=p[0],I=p[1],h=(0,m.useRef)(),s=(0,m.useRef)(),V=[{title:"\u89C6\u9891",dataIndex:"userId",valueType:"textarea",copyable:!0,search:!1,render:function(t,r){return(0,a.jsx)(W,{videos:r.materialVideoInfoList})}},{title:"\u4E3B\u64ADID",dataIndex:"userId",valueType:"textarea",copyable:!0},{title:"\u6A21\u5361\u6635\u79F0",dataIndex:"name",valueType:"textarea"},{title:"\u4E3B\u64AD\u6635\u79F0",dataIndex:"nickname",valueType:"textarea",search:!1},{title:"\u81EA\u8EAB\u4F18\u52BF",dataIndex:"advantages",valueType:"textarea",search:!1,width:200},{title:"\u6A21\u5361\u72B6\u6001",dataIndex:"isOpen",valueType:"textarea",renderText:function(t){var r;return(r=o.find(function(n){return n.value===t}))===null||r===void 0?void 0:r.label},renderFormItem:function(){return(0,a.jsx)(c.Z,{showSearch:!0,allowClear:!0,options:o})},search:!1},{title:"\u4E3B\u64AD\u6A21\u5361\u72B6\u6001",dataIndex:"userOpen",valueType:"textarea",renderText:function(t){var r;return(r=o.find(function(n){return n.value===t}))===null||r===void 0?void 0:r.label},renderFormItem:function(){return(0,a.jsx)(c.Z,{showSearch:!0,allowClear:!0,options:o})}},{title:"\u540E\u53F0\u6A21\u5361\u72B6\u6001",dataIndex:"adminOpen",valueType:"textarea",renderText:function(t){var r;return(r=o.find(function(n){return n.value===t}))===null||r===void 0?void 0:r.label},renderFormItem:function(){return(0,a.jsx)(c.Z,{showSearch:!0,allowClear:!0,options:o})}},{title:"\u610F\u5411\u57CE\u5E02",dataIndex:"cityCode",valueType:"textarea",renderText:function(t){return F.JO.get(t)},renderFormItem:function(){return(0,a.jsx)(c.Z,{showSearch:!0,allowClear:!0,options:F.ZD,filterOption:function(r,n){var i;return((i=n==null?void 0:n.label)!==null&&i!==void 0?i:"").toLowerCase().includes(r.toLowerCase())}})}},{title:"\u64AD\u8FC7\u7684\u54C1\u7C7B",dataIndex:"workedSecCategoryStr",valueType:"textarea",search:!1,width:200},{title:"\u521B\u5EFA\u65F6\u95F4",dataIndex:"created",valueType:"dateTime",renderText:function(t){return R()(Number(t)).format(w.T)},search:!1,sorter:!0},{title:"\u4FEE\u6539\u65F6\u95F4",dataIndex:"updated",valueType:"dateTime",renderText:function(t){return R()(Number(t)).format(w.T)},search:!1,sorter:!0},{title:"\u64CD\u4F5C",valueType:"option",fixed:"right",width:100,align:"center",render:function(t,r){return(0,a.jsx)("a",{onClick:function(){f(!0),I(r)},children:"\u4FEE\u6539"},"config")}}];return(0,a.jsxs)(L._z,{children:[(0,a.jsx)(U.Z,{headerTitle:"\u67E5\u8BE2\u8868\u683C",actionRef:h,rowKey:"key",search:{labelWidth:120,collapsed:!1,collapseRender:!1},request:P._h,columns:V,scroll:{x:"max-content"}}),(0,a.jsx)(E.Y,{title:"\u66F4\u65B0\u6A21\u5361\u4FE1\u606F",width:"400px",formRef:s,open:x,onOpenChange:f,onFinish:function(){var l=A()(O()().mark(function t(r){var n,i,S;return O()().wrap(function(u){for(;;)switch(u.prev=u.next){case 0:return n={id:d.id,adminOpen:Number(r.adminOpen)},console.log("update confirm",r,n),u.prev=2,u.next=5,(0,P.MP)(n);case 5:(i=h.current)===null||i===void 0||i.reload(),(S=s.current)===null||S===void 0||S.resetFields(),u.next=11;break;case 9:u.prev=9,u.t0=u.catch(2);case 11:f(!1);case 12:case"end":return u.stop()}},t,null,[[2,9]])}));return function(t){return l.apply(this,arguments)}}(),children:(0,a.jsx)(B.Z,{name:"adminOpen",label:"\u540E\u53F0\u6A21\u5361\u72B6\u6001",options:o,rules:[{required:!0,message:"\u5FC5\u586B\u9879"}]})})]})},J=z}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
!function(){"use strict";var t="/".replace(/([^/])$/,"$1/"),e=location.pathname,n=e.startsWith(t)&&decodeURI("/".concat(e.slice(t.length)));if(n){var a=document,c=a.head,r=a.createElement.bind(a),i=function(t,e,n){var a,c=e.r[t]||(null===(a=Object.entries(e.r).find((function(e){var n=e[0];return new RegExp("^".concat(n.replace(/\/:[^/]+/g,"/[^/]+").replace("/*","/.+"),"$")).test(t)})))||void 0===a?void 0:a[1]);return null==c?void 0:c.map((function(t){var a=e.f[t][1],c=e.f[t][0];return{type:c.split(".").pop(),url:"".concat(n.publicPath).concat(c),attrs:[["data-".concat(e.b),"".concat(e.p,":").concat(a)]]}}))}(n,{"p":"boluo-admin","b":"webpack","f":[["33.bc87f474.async.js",33],["p__user__login__index.ddbeca1d.async.js",37],["p__table-list__job__index.333a69f3.async.js",209],["293.7c030207.async.js",293],["t__plugin-layout__Layout.5012e1ab.chunk.css",301],["t__plugin-layout__Layout.677fcfb3.async.js",301],["315.4c4d15c9.async.js",315],["387.19aacc56.async.js",387],["436.8ec0b242.async.js",436],["p__table-list__anchor-group__index.f3030792.async.js",525],["p__table-list__declaration__index.dec5b489.async.js",544],["p__404.affc3845.async.js",571],["p__table-list__group__index.6298cbff.async.js",583],["595.44b14770.async.js",595],["p__table-list__publisher__index.b32f1fc0.async.js",845],["849.cbcb9a3b.async.js",849],["p__table-list__anchor__index.0eb8e3c6.async.js",880],["p__table-list__material__index.99f2d4a2.async.js",935]],"r":{"/*":[8,11],"/":[3,4,5,6,8],"/user/login":[1,3,13],"/list/job":[0,2,3,7,8,13,4,5,6],"/list/group":[0,3,7,8,12,13,4,5,6],"/list/anchor-group":[3,7,8,9,13,4,5,6],"/list/publisher":[3,7,8,13,14,4,5,6],"/list/anchor":[0,3,7,8,13,16,4,5,6],"/list/declaration":[0,3,7,8,10,13,4,5,6],"/list/resume":[0,3,7,8,13,17,4,5,6]}},{publicPath:"/"});null==i||i.forEach((function(t){var e,n=t.type,a=t.url;if("js"===n)(e=r("script")).src=a,e.async=!0;else{if("css"!==n)return;(e=r("link")).href=a,e.rel="preload",e.as="style"}t.attrs.forEach((function(t){e.setAttribute(t[0],t[1]||"")})),c.appendChild(e)}))}}();

5
未命名文件夹/dist/pro_icon.svg vendored Normal file
View File

@ -0,0 +1,5 @@
<svg width="42" height="42" xmlns="http://www.w3.org/2000/svg">
<g>
<path fill="#070707" d="m6.717392,13.773912l5.6,0c2.8,0 4.7,1.9 4.7,4.7c0,2.8 -2,4.7 -4.9,4.7l-2.5,0l0,4.3l-2.9,0l0,-13.7zm2.9,2.2l0,4.9l1.9,0c1.6,0 2.6,-0.9 2.6,-2.4c0,-1.6 -0.9,-2.4 -2.6,-2.4l-1.9,0l0,-0.1zm8.9,11.5l2.7,0l0,-5.7c0,-1.4 0.8,-2.3 2.2,-2.3c0.4,0 0.8,0.1 1,0.2l0,-2.4c-0.2,-0.1 -0.5,-0.1 -0.8,-0.1c-1.2,0 -2.1,0.7 -2.4,2l-0.1,0l0,-1.9l-2.7,0l0,10.2l0.1,0zm11.7,0.1c-3.1,0 -5,-2 -5,-5.3c0,-3.3 2,-5.3 5,-5.3s5,2 5,5.3c0,3.4 -1.9,5.3 -5,5.3zm0,-2.1c1.4,0 2.2,-1.1 2.2,-3.2c0,-2 -0.8,-3.2 -2.2,-3.2c-1.4,0 -2.2,1.2 -2.2,3.2c0,2.1 0.8,3.2 2.2,3.2z" class="st0" id="Ant-Design-Pro"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 677 B

View File

@ -0,0 +1,202 @@
/**
* loading 占位
* 解决首次加载时白屏的问题
*/
(function () {
const _root = document.querySelector('#root');
if (_root && _root.innerHTML === '') {
_root.innerHTML = `
<style>
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
}
#root {
background-repeat: no-repeat;
background-size: 100% auto;
}
.loading-title {
font-size: 1.1rem;
}
.loading-sub-title {
margin-top: 20px;
font-size: 1rem;
color: #888;
}
.page-loading-warp {
display: flex;
align-items: center;
justify-content: center;
padding: 26px;
}
.ant-spin {
position: absolute;
display: none;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
color: #1890ff;
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
text-align: center;
list-style: none;
opacity: 0;
-webkit-transition: -webkit-transform 0.3s
cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: -webkit-transform 0.3s
cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
-webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
-webkit-font-feature-settings: "tnum";
font-feature-settings: "tnum";
}
.ant-spin-spinning {
position: static;
display: inline-block;
opacity: 1;
}
.ant-spin-dot {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
font-size: 20px;
}
.ant-spin-dot-item {
position: absolute;
display: block;
width: 9px;
height: 9px;
background-color: #1890ff;
border-radius: 100%;
-webkit-transform: scale(0.75);
-ms-transform: scale(0.75);
transform: scale(0.75);
-webkit-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
transform-origin: 50% 50%;
opacity: 0.3;
-webkit-animation: antspinmove 1s infinite linear alternate;
animation: antSpinMove 1s infinite linear alternate;
}
.ant-spin-dot-item:nth-child(1) {
top: 0;
left: 0;
}
.ant-spin-dot-item:nth-child(2) {
top: 0;
right: 0;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.ant-spin-dot-item:nth-child(3) {
right: 0;
bottom: 0;
-webkit-animation-delay: 0.8s;
animation-delay: 0.8s;
}
.ant-spin-dot-item:nth-child(4) {
bottom: 0;
left: 0;
-webkit-animation-delay: 1.2s;
animation-delay: 1.2s;
}
.ant-spin-dot-spin {
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-animation: antrotate 1.2s infinite linear;
animation: antRotate 1.2s infinite linear;
}
.ant-spin-lg .ant-spin-dot {
width: 32px;
height: 32px;
font-size: 32px;
}
.ant-spin-lg .ant-spin-dot i {
width: 14px;
height: 14px;
}
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.ant-spin-blur {
background: #fff;
opacity: 0.5;
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 1;
}
}
@keyframes antSpinMove {
to {
opacity: 1;
}
}
@-webkit-keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
</style>
<div style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
min-height: 362px;
">
<div class="page-loading-warp">
<div class="ant-spin ant-spin-lg ant-spin-spinning">
<span class="ant-spin-dot ant-spin-dot-spin">
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
</span>
</div>
</div>
<div class="loading-title">
正在加载资源
</div>
<div class="loading-sub-title">
初次加载资源可能需要较多时间 请耐心等待
</div>
</div>
`;
}
})();

View File

@ -0,0 +1 @@
@media screen and (max-width: 480px){.umi-plugin-layout-container{width:100%!important}.umi-plugin-layout-container>*{border-radius:0!important}}.umi-plugin-layout-menu .anticon{margin-right:8px}.umi-plugin-layout-menu .ant-dropdown-menu-item{min-width:160px}.umi-plugin-layout-right{display:flex!important;float:right;height:100%;margin-left:auto;overflow:hidden}.umi-plugin-layout-right .umi-plugin-layout-action{display:flex;align-items:center;height:100%;padding:0 12px;cursor:pointer;transition:all .3s}.umi-plugin-layout-right .umi-plugin-layout-action>i{color:#ffffffd9;vertical-align:middle}.umi-plugin-layout-right .umi-plugin-layout-action:hover,.umi-plugin-layout-right .umi-plugin-layout-action.opened{background:#00000006}.umi-plugin-layout-right .umi-plugin-layout-search{padding:0 12px}.umi-plugin-layout-right .umi-plugin-layout-search:hover{background:transparent}.umi-plugin-layout-name{margin-left:8px}.umi-plugin-layout-name.umi-plugin-layout-hide-avatar-img{margin-left:0}

File diff suppressed because one or more lines are too long

327
未命名文件夹/dist/umi.6921976f.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
html,body,#root{height:100%;margin:0;padding:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji"}.colorWeak{filter:invert(80%)}.ant-layout{min-height:100vh}.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed{left:unset}canvas{display:block}body{text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}ul,ol{list-style:none}@media (max-width: 768px){.ant-table{width:100%;overflow-x:auto}.ant-table-thead>tr>th,.ant-table-tbody>tr>th,.ant-table-thead>tr>td,.ant-table-tbody>tr>td{white-space:pre}.ant-table-thead>tr>th>span,.ant-table-tbody>tr>th>span,.ant-table-thead>tr>td>span,.ant-table-tbody>tr>td>span{display:block}}html,body{width:100%;height:100%}input::-ms-clear,input::-ms-reveal{display:none}*,*:before,*:after{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{margin:0}[tabindex="-1"]:focus{outline:none}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5em;font-weight:500}p{margin-top:0;margin-bottom:1em}abbr[title],abbr[data-original-title]{text-decoration:underline;text-decoration:underline dotted;border-bottom:0;cursor:help}address{margin-bottom:1em;font-style:normal;line-height:inherit}input[type=text],input[type=password],input[type=number],textarea{-webkit-appearance:none}ol,ul,dl{margin-top:0;margin-bottom:1em}ol ol,ul ul,ol ul,ul ol{margin-bottom:0}dt{font-weight:500}dd{margin-bottom:.5em;margin-left:0}blockquote{margin:0 0 1em}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}pre,code,kbd,samp{font-size:1em;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace}pre{margin-top:0;margin-bottom:1em;overflow:auto}figure{margin:0 0 1em}img{vertical-align:middle;border-style:none}a,area,button,[role=button],input:not([type=range]),label,select,summary,textarea{touch-action:manipulation}table{border-collapse:collapse}caption{padding-top:.75em;padding-bottom:.3em;text-align:left;caption-side:bottom}input,button,select,optgroup,textarea{margin:0;color:inherit;font-size:inherit;font-family:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}button,html [type=button],[type=reset],[type=submit]{-webkit-appearance:button}button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{padding:0;border-style:none}input[type=radio],input[type=checkbox]{box-sizing:border-box;padding:0}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;margin:0;padding:0;border:0}legend{display:block;width:100%;max-width:100%;margin-bottom:.5em;padding:0;color:inherit;font-size:1.5em;line-height:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item}template{display:none}[hidden]{display:none!important}mark{padding:.2em;background-color:#feffe6}