Compare commits

..

5 Commits

Author SHA1 Message Date
maxjhandsome beb78e5ea1 ADD file via upload 2025-12-31 09:06:18 +08:00
谢思 e2ea1595f3 首页各模块增加入口 2025-12-04 10:33:56 +08:00
谢思 97ae21fbbe 代码库主页增加缺省图 2025-12-04 10:33:36 +08:00
谢思 a2be5948b5 供应链增加uid传参 2025-12-04 10:32:48 +08:00
谢思 a223193b18 供应链banner图替换 2025-12-04 10:32:25 +08:00
575 changed files with 607 additions and 31685 deletions

View File

@ -1,17 +0,0 @@
name: Deploy API
on:
workflow_call:
inputs:
env:
required: true
type: string
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: https://gitlink.org.cn/actions/checkout@v4
- name: Deploy API
run: |
echo "Deploying API to ${{ inputs.env }}"

View File

@ -1,16 +0,0 @@
name: Deploy Web
on:
workflow_call:
inputs:
env:
required: true
type: string
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: https://gitlink.org.cn/actions/checkout@v4
- name: Deploy Web
run: |
echo "Deploying Web to ${{ inputs.env }}"

View File

@ -1,58 +0,0 @@
name: Context testing
on: push
jobs:
dump_contexts_to_log:
runs-on: ubuntu-latest
steps:
- run: echo "SELECTED_COLOR=green" >> "$GITHUB_ENV"
- run: echo "The selected color is ${{env.SELECTED_COLOR}}"
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Dump job context
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: |
echo "$JOB_CONTEXT"
echo "adsfsf=========${{ github.action }}"
- name: env
run: env
- name: Dump steps context
id: steps_context
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: |
echo "$STEPS_CONTEXT"
echo "adsfsf=========${{ github.action }}"
echo "ACTIONS_RUNTIME_TOKEN====================${{ACTIONS_RUNTIME_TOKEN}}"
- name: Dump runner context
env:
RUNNER_CONTEXT: ${{ toJson(runner) }}
run: echo "$RUNNER_CONTEXT"
- name: Dump needs context
env:
NEEDS_CONTEXT: ${{ toJson(needs) }}
run: echo "$NEEDS_CONTEXT"
- name: Dump strategy context
env:
STRATEGY_CONTEXT: ${{ toJson(strategy) }}
run: echo "$STRATEGY_CONTEXT"
- name: Dump matrix context
env:
MATRIX_CONTEXT: ${{ toJson(matrix) }}
run: echo "$MATRIX_CONTEXT"
- name: Dump steps context2
id: steps_context2
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: |
echo "$STEPS_CONTEXT"
echo "adsfsf=========${{ github.action }}"
echo "ACTIONS_RUNTIME_TOKEN====================${{ACTIONS_RUNTIME_TOKEN}}"
- name: Dump GitHub context2
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: sleep 33 && echo "$GITHUB_CONTEXT"

View File

@ -1,12 +0,0 @@
name: Context testing
on: push
jobs:
run-01:
uses: ./.gitea/workflows/上下文信息.yml
run-02:
uses: ./.gitea/workflows/声明流水线.yml
run-03:
uses: ./.gitea/workflows/deploy-api.yml
run-04:
uses: ./.gitea/workflows/deploy-web.yml

View File

@ -1,58 +0,0 @@
name: Context testing
on: push
jobs:
dump_contexts_to_log:
runs-on: ubuntu-latest
steps:
- run: echo "SELECTED_COLOR=green" >> "$GITHUB_ENV"
- run: echo "The selected color is ${{env.SELECTED_COLOR}}"
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Dump job context
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: |
echo "$JOB_CONTEXT"
echo "adsfsf=========${{ github.action }}"
- name: env
run: env
- name: Dump steps context
id: steps_context
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: |
echo "$STEPS_CONTEXT"
echo "adsfsf=========${{ github.action }}"
echo "ACTIONS_RUNTIME_TOKEN====================${{ACTIONS_RUNTIME_TOKEN}}"
- name: Dump runner context
env:
RUNNER_CONTEXT: ${{ toJson(runner) }}
run: echo "$RUNNER_CONTEXT"
- name: Dump needs context
env:
NEEDS_CONTEXT: ${{ toJson(needs) }}
run: echo "$NEEDS_CONTEXT"
- name: Dump strategy context
env:
STRATEGY_CONTEXT: ${{ toJson(strategy) }}
run: echo "$STRATEGY_CONTEXT"
- name: Dump matrix context
env:
MATRIX_CONTEXT: ${{ toJson(matrix) }}
run: echo "$MATRIX_CONTEXT"
- name: Dump steps context2
id: steps_context2
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: |
echo "$STEPS_CONTEXT"
echo "adsfsf=========${{ github.action }}"
echo "ACTIONS_RUNTIME_TOKEN====================${{ACTIONS_RUNTIME_TOKEN}}"
- name: Dump GitHub context2
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"

View File

@ -56,4 +56,4 @@ export const deepRoutes = [
]
// 不进行组织或个人判断
export const specialRoute = ["ossRepo", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot","zone", "undefined", "admins", "oauth", "competitions", "hiagent","sf", "development", "governance","explore"]
export const specialRoute = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot","zone", "undefined", "admins", "oauth", "competitions", "hiagent","softwareFactory", "development", "governance"]

BIN
nexus数据导入表.xlsx Normal file

Binary file not shown.

View File

@ -55,7 +55,6 @@
"express": "^4.18.2",
"flv.js": "^1.5.0",
"fs-extra": "3.0.1",
"gsap": "^3.14.2",
"http-proxy-middleware": "^2.0.6",
"i18next": "^23.4.5",
"immutability-helper": "^2.6.6",
@ -83,7 +82,6 @@
"numeral": "^2.0.6",
"nvm": "0.0.4",
"object-assign": "4.1.1",
"p-queue": "^9.2.0",
"papaparse": "^5.3.2",
"pinyin": "^4.0.0-alpha.0",
"postcss-flexbugs-fixes": "3.2.0",
@ -139,7 +137,6 @@
"showdown": "^1.9.1",
"showdown-katex": "^0.8.0",
"slick-carousel": "^1.8.1",
"spark-md5": "^3.0.2",
"store": "^2.0.12",
"styled-components": "^4.4.1",
"webpack-node-externals": "^3.0.0",
@ -181,7 +178,7 @@
"<rootDir>/src/**/?(*.)(spec|test).{js,jsx,mjs}"
],
"testEnvironment": "node",
"testURL": "http://172.20.32.203:4000",
"testURL": "http://localhost",
"transform": {
"^.+\\.(js|jsx|mjs)$": "<rootDir>/node_modules/babel-jest",
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
@ -206,7 +203,7 @@
"eslintConfig": {
"extends": "react-app"
},
"proxy": "http://172.20.32.203:4000/",
"proxy": "https://testforgeplus.trustie.net/",
"port": "3007",
"devDependencies": {
"@babel/core": "^7.23.6",

View File

@ -16,22 +16,10 @@
font-family: "alibabaReg";
src: url("../fonts/ALIBABA-PUHUITI-REGULAR.TTF");
}
@font-face {
font-family: "alibabaBold";
src: url("../fonts/ALIBABA-PUHUITI-BOLD.TTF");
}
@font-face {
font-family: "ziyouwenyihei";
src: url("../fonts/ziyouwenyihei.ttf");
}
@font-face {
font-family: "DouyinSansBold";
src: url("../fonts/DouyinSansBold.ttf");
}
@font-face {
font-family: "DingTalk JinBuTi";
src: url("../fonts/DingTalk_JinBuTi.ttf");
}
.header {
width: 100%;
height: 51px;
@ -1409,10 +1397,6 @@ a.edu-txt-w80,
font-size: 20px !important
}
.font-21 {
font-size: 21px;
}
.font-22 {
font-size: 22px !important
}
@ -1449,10 +1433,6 @@ a.edu-txt-w80,
font-size: 36px !important
}
.font-38 {
font-size: 38px !important
}
.font-40 {
font-size: 40px !important
}
@ -1461,10 +1441,6 @@ a.edu-txt-w80,
font-size: 50px !important
}
.font-60 {
font-size: 60px !important
}
.font-80 {
font-size: 80px !important
}
@ -1486,14 +1462,6 @@ a.decoration {
font-weight: 400
}
.mt-5{
margin-top: -5px;
}
.mt-8{
margin-top: -8px;
}
.mt1 {
margin-top: 1px
}
@ -1650,10 +1618,6 @@ a.decoration {
margin-bottom: 7px
}
.mb8 {
margin-bottom: 8px
}
.mb10 {
margin-bottom: 10px
}
@ -1694,10 +1658,6 @@ a.decoration {
margin-bottom: 30px !important
}
.mb35 {
margin-bottom: 35px !important
}
.mb40 {
margin-bottom: 40px !important
}
@ -1782,10 +1742,6 @@ a.decoration {
margin-left: 22px
}
.ml24 {
margin-left: 24px
}
.ml25 {
margin-left: 25px
}
@ -1822,18 +1778,10 @@ a.decoration {
margin-left: 85px
}
.ml100 {
margin-left: 100px
}
.ml180 {
margin-left: 180px
}
.mr2 {
margin-right: 2px
}
.mr3 {
margin-right: 3px
}
@ -1914,10 +1862,6 @@ a.decoration {
padding-top: 5px !important
}
.pt8 {
padding-top: 8px !important
}
.pt10 {
padding-top: 10px
}
@ -1958,10 +1902,6 @@ a.decoration {
padding-top: 60px
}
.pt70 {
padding-top: 70px
}
.pt80 {
padding-top: 80px
}
@ -1983,7 +1923,11 @@ a.decoration {
}
.pb25 {
padding-bottom: 25px
padding-bottom: 20px
}
.pb25 {
padding-bottom: 20px
}
.pb28 {
@ -2010,18 +1954,6 @@ a.decoration {
padding-bottom: 60px
}
.pb70 {
padding-bottom: 70px
}
.pb80 {
padding-bottom: 80px
}
.pb90 {
padding-bottom: 90px
}
.pb100 {
padding-bottom: 100px
}
@ -2050,14 +1982,6 @@ a.decoration {
padding-right: 35px !important
}
.pr50 {
padding-right: 50px !important
}
.pr80 {
padding-right: 80px !important
}
.pl0 {
padding-left: 0 !important
}
@ -2106,10 +2030,6 @@ a.decoration {
padding-left: 40px
}
.pl45 {
padding-left: 45px
}
.pl50 {
padding-left: 50px
}
@ -2191,11 +2111,6 @@ a.decoration {
box-sizing: border-box
}
.padding25-30{
padding: 25px 30px;
box-sizing: border-box
}
.padding30 {
padding: 30px;
box-sizing: border-box
@ -2221,10 +2136,6 @@ a.decoration {
box-sizing: border-box
}
.line-normal{
line-height: normal;
}
.lineh-12 {
line-height: 12px !important
}
@ -2261,14 +2172,6 @@ a.decoration {
line-height: 40px !important
}
.lineh-50 {
line-height: 50px !important
}
.lineh-80 {
line-height: 80px !important
}
.break_word {
word-break: break-all;
word-wrap: break-word

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 2340181 */
src: url('iconfont.woff2?t=1784688510240') format('woff2'),
url('iconfont.woff?t=1784688510240') format('woff'),
url('iconfont.ttf?t=1784688510240') format('truetype');
src: url('iconfont.woff2?t=1764752922359') format('woff2'),
url('iconfont.woff?t=1764752922359') format('woff'),
url('iconfont.ttf?t=1764752922359') format('truetype');
}
.iconfont {
@ -13,314 +13,6 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-zhinengti:before {
content: "\ea0f";
}
.icon-a-fabu1:before {
content: "\ea0d";
}
.icon-a-search-ai6c9d7b621:before {
content: "\ea0e";
}
.icon-remenhuati:before {
content: "\e9df";
}
.icon-chengguolingyu:before {
content: "\ea09";
}
.icon-chengguoleixing:before {
content: "\ea0a";
}
.icon-chengguolaiyuan:before {
content: "\ea0b";
}
.icon-zhinengzhushou:before {
content: "\ea08";
}
.icon-shine-search:before {
content: "\ea07";
}
.icon-zuojiantou3:before {
content: "\ea05";
}
.icon-youjiantou2:before {
content: "\ea06";
}
.icon-fanhuagongju:before {
content: "\ea02";
}
.icon-jingxuanxiangmu1:before {
content: "\ea03";
}
.icon-gengduo2:before {
content: "\ea04";
}
.icon-shengwuyiyao:before {
content: "\e9f5";
}
.icon-youjiantou1:before {
content: "\e9fa";
}
.icon-zuojiantou2:before {
content: "\e9fd";
}
.icon-daqihaiyanghuanjing:before {
content: "\e9fe";
}
.icon-tianwendiliwuli:before {
content: "\e9ff";
}
.icon-xinnengyuanxincailiao:before {
content: "\ea00";
}
.icon-gongyeshejizhizao:before {
content: "\ea01";
}
.icon-tiezi:before {
content: "\e9f4";
}
.icon-yulan:before {
content: "\e9f1";
}
.icon-arrright:before {
content: "\e9fc";
}
.icon-yunxiaogongju:before {
content: "\e9f6";
}
.icon-daimazhijian:before {
content: "\e9f7";
}
.icon-yidongkaifa:before {
content: "\e9f8";
}
.icon-qita:before {
content: "\e9f9";
}
.icon-meitichuli:before {
content: "\e9fb";
}
.icon-skill:before {
content: "\e9f3";
}
.icon-daimaku1:before {
content: "\e9f2";
}
.icon-dianzan5:before {
content: "\e9ee";
}
.icon-duomotaimoxing:before {
content: "\e9ef";
}
.icon-a-kexueyanjiuhejishufuwuye-1:before {
content: "\e9f0";
}
.icon-yinpin-1:before {
content: "\e9e2";
}
.icon-shujuji-4:before {
content: "\e9e4";
}
.icon-shujuji:before {
content: "\e9e5";
}
.icon-shujuyuanmoxing:before {
content: "\e9e6";
}
.icon-shujuji-3:before {
content: "\e9e7";
}
.icon-a-shujuji5:before {
content: "\e9e8";
}
.icon-a-shujuji-6:before {
content: "\e9e9";
}
.icon-shipinbofang-1:before {
content: "\e9ea";
}
.icon-moxingku:before {
content: "\e9eb";
}
.icon-a-shijianzhongbiao:before {
content: "\e9ec";
}
.icon-remenmoxing-2:before {
content: "\e9ed";
}
.icon-a-Dshujukukaifagongju:before {
content: "\e9cd";
}
.icon-ziranyuyanchuli-2:before {
content: "\e9db";
}
.icon-a-zu18214:before {
content: "\e9dc";
}
.icon-zizhushujuji:before {
content: "\e9dd";
}
.icon-yuyin-1:before {
content: "\e9de";
}
.icon-a-tuxiang-1:before {
content: "\e9e0";
}
.icon-xiazai3:before {
content: "\e9e1";
}
.icon-shijian1:before {
content: "\e9cb";
}
.icon-liulanliang:before {
content: "\e9cc";
}
.icon-guanbi2:before {
content: "\e9ca";
}
.icon-zhanshishangxian1:before {
content: "\e9d3";
}
.icon-tianxiexinxi:before {
content: "\e9d8";
}
.icon-tijiaoshenqing1:before {
content: "\e9d9";
}
.icon-guanliyuanshenhe1:before {
content: "\e9da";
}
.icon-sixin2:before {
content: "\e9e3";
}
.icon-dianzan4:before {
content: "\e9d6";
}
.icon-liulanshu:before {
content: "\e9d7";
}
.icon-tiezishu:before {
content: "\e9ce";
}
.icon-yanjiulingyu:before {
content: "\e9cf";
}
.icon-a-xiugai3:before {
content: "\e9d0";
}
.icon-yitijiaoderuzhuxinxi:before {
content: "\e9d1";
}
.icon-sixin:before {
content: "\e9d2";
}
.icon-gerenjianjie:before {
content: "\e9d4";
}
.icon-guanzhu1:before {
content: "\e9d5";
}
.icon-woyefatie:before {
content: "\e9c9";
}
.icon-yingyongmoban:before {
content: "\e9c8";
}
.icon-zhuangtai:before {
content: "\e9c5";
}
.icon-fuzhi-shanchu1:before {
content: "\e9c6";
}
.icon-mobanguanli:before {
content: "\e9c7";
}
.icon-svg_jiantou1:before {
content: "\e9c3";
}
.icon-zuojiantou1:before {
content: "\e9c4";
}
.icon-jiantou4:before {
content: "\e9c2";
}
.icon-hezuohuoban:before {
content: "\e9bf";
}

File diff suppressed because one or more lines are too long

View File

@ -5,545 +5,6 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "48129347",
"name": "智能体",
"font_class": "zhinengti",
"unicode": "ea0f",
"unicode_decimal": 59919
},
{
"icon_id": "48114624",
"name": "发布 (1)",
"font_class": "a-fabu1",
"unicode": "ea0d",
"unicode_decimal": 59917
},
{
"icon_id": "48114623",
"name": "search-ai.6c9d7b62 (1)",
"font_class": "a-search-ai6c9d7b621",
"unicode": "ea0e",
"unicode_decimal": 59918
},
{
"icon_id": "48114629",
"name": "热门话题",
"font_class": "remenhuati",
"unicode": "e9df",
"unicode_decimal": 59871
},
{
"icon_id": "47969738",
"name": "成果领域",
"font_class": "chengguolingyu",
"unicode": "ea09",
"unicode_decimal": 59913
},
{
"icon_id": "47969739",
"name": "成果类型",
"font_class": "chengguoleixing",
"unicode": "ea0a",
"unicode_decimal": 59914
},
{
"icon_id": "47969740",
"name": "成果来源",
"font_class": "chengguolaiyuan",
"unicode": "ea0b",
"unicode_decimal": 59915
},
{
"icon_id": "45990748",
"name": "智能助手",
"font_class": "zhinengzhushou",
"unicode": "ea08",
"unicode_decimal": 59912
},
{
"icon_id": "47826921",
"name": "shine-search",
"font_class": "shine-search",
"unicode": "ea07",
"unicode_decimal": 59911
},
{
"icon_id": "47798947",
"name": "左箭头",
"font_class": "zuojiantou3",
"unicode": "ea05",
"unicode_decimal": 59909
},
{
"icon_id": "47798946",
"name": "右箭头",
"font_class": "youjiantou2",
"unicode": "ea06",
"unicode_decimal": 59910
},
{
"icon_id": "47778051",
"name": "泛化工具",
"font_class": "fanhuagongju",
"unicode": "ea02",
"unicode_decimal": 59906
},
{
"icon_id": "47778050",
"name": "精选项目",
"font_class": "jingxuanxiangmu1",
"unicode": "ea03",
"unicode_decimal": 59907
},
{
"icon_id": "47778049",
"name": "更多",
"font_class": "gengduo2",
"unicode": "ea04",
"unicode_decimal": 59908
},
{
"icon_id": "47778058",
"name": "生物医药",
"font_class": "shengwuyiyao",
"unicode": "e9f5",
"unicode_decimal": 59893
},
{
"icon_id": "47778057",
"name": "右箭头",
"font_class": "youjiantou1",
"unicode": "e9fa",
"unicode_decimal": 59898
},
{
"icon_id": "47778056",
"name": "左箭头",
"font_class": "zuojiantou2",
"unicode": "e9fd",
"unicode_decimal": 59901
},
{
"icon_id": "47778052",
"name": "大气海洋环境",
"font_class": "daqihaiyanghuanjing",
"unicode": "e9fe",
"unicode_decimal": 59902
},
{
"icon_id": "47778055",
"name": "天文地理物理",
"font_class": "tianwendiliwuli",
"unicode": "e9ff",
"unicode_decimal": 59903
},
{
"icon_id": "47778054",
"name": "新能源新材料",
"font_class": "xinnengyuanxincailiao",
"unicode": "ea00",
"unicode_decimal": 59904
},
{
"icon_id": "47778053",
"name": "工业设计制造",
"font_class": "gongyeshejizhizao",
"unicode": "ea01",
"unicode_decimal": 59905
},
{
"icon_id": "47756092",
"name": "帖子",
"font_class": "tiezi",
"unicode": "e9f4",
"unicode_decimal": 59892
},
{
"icon_id": "47540661",
"name": "预览",
"font_class": "yulan",
"unicode": "e9f1",
"unicode_decimal": 59889
},
{
"icon_id": "40883122",
"name": "长箭头-copy",
"font_class": "arrright",
"unicode": "e9fc",
"unicode_decimal": 59900
},
{
"icon_id": "47487774",
"name": "云效工具",
"font_class": "yunxiaogongju",
"unicode": "e9f6",
"unicode_decimal": 59894
},
{
"icon_id": "47487775",
"name": "代码质检",
"font_class": "daimazhijian",
"unicode": "e9f7",
"unicode_decimal": 59895
},
{
"icon_id": "47487773",
"name": "移动开发",
"font_class": "yidongkaifa",
"unicode": "e9f8",
"unicode_decimal": 59896
},
{
"icon_id": "47487772",
"name": "其他",
"font_class": "qita",
"unicode": "e9f9",
"unicode_decimal": 59897
},
{
"icon_id": "47487770",
"name": "媒体处理",
"font_class": "meitichuli",
"unicode": "e9fb",
"unicode_decimal": 59899
},
{
"icon_id": "47487669",
"name": "skill",
"font_class": "skill",
"unicode": "e9f3",
"unicode_decimal": 59891
},
{
"icon_id": "47487670",
"name": "代码库",
"font_class": "daimaku1",
"unicode": "e9f2",
"unicode_decimal": 59890
},
{
"icon_id": "47470076",
"name": "点赞",
"font_class": "dianzan5",
"unicode": "e9ee",
"unicode_decimal": 59886
},
{
"icon_id": "47470075",
"name": "多模态模型",
"font_class": "duomotaimoxing",
"unicode": "e9ef",
"unicode_decimal": 59887
},
{
"icon_id": "47470074",
"name": "科学研究和技术服务业 -1",
"font_class": "a-kexueyanjiuhejishufuwuye-1",
"unicode": "e9f0",
"unicode_decimal": 59888
},
{
"icon_id": "47470091",
"name": "音频-1",
"font_class": "yinpin-1",
"unicode": "e9e2",
"unicode_decimal": 59874
},
{
"icon_id": "47470090",
"name": "数据集-4",
"font_class": "shujuji-4",
"unicode": "e9e4",
"unicode_decimal": 59876
},
{
"icon_id": "47470088",
"name": "数据集",
"font_class": "shujuji",
"unicode": "e9e5",
"unicode_decimal": 59877
},
{
"icon_id": "47470089",
"name": "数据源模型",
"font_class": "shujuyuanmoxing",
"unicode": "e9e6",
"unicode_decimal": 59878
},
{
"icon_id": "47470087",
"name": "数据集-3",
"font_class": "shujuji-3",
"unicode": "e9e7",
"unicode_decimal": 59879
},
{
"icon_id": "47470086",
"name": "数据集 (5)",
"font_class": "a-shujuji5",
"unicode": "e9e8",
"unicode_decimal": 59880
},
{
"icon_id": "47470083",
"name": "数据集 -6",
"font_class": "a-shujuji-6",
"unicode": "e9e9",
"unicode_decimal": 59881
},
{
"icon_id": "47470084",
"name": "视频播放-1",
"font_class": "shipinbofang-1",
"unicode": "e9ea",
"unicode_decimal": 59882
},
{
"icon_id": "47470081",
"name": "模型库",
"font_class": "moxingku",
"unicode": "e9eb",
"unicode_decimal": 59883
},
{
"icon_id": "47470080",
"name": "时间 钟表",
"font_class": "a-shijianzhongbiao",
"unicode": "e9ec",
"unicode_decimal": 59884
},
{
"icon_id": "47470079",
"name": "热门模型-2",
"font_class": "remenmoxing-2",
"unicode": "e9ed",
"unicode_decimal": 59885
},
{
"icon_id": "47470107",
"name": "D+数据库开发工具",
"font_class": "a-Dshujukukaifagongju",
"unicode": "e9cd",
"unicode_decimal": 59853
},
{
"icon_id": "47470101",
"name": "自然语言处理-2",
"font_class": "ziranyuyanchuli-2",
"unicode": "e9db",
"unicode_decimal": 59867
},
{
"icon_id": "47470102",
"name": "组 18214",
"font_class": "a-zu18214",
"unicode": "e9dc",
"unicode_decimal": 59868
},
{
"icon_id": "47470098",
"name": "自助数据集",
"font_class": "zizhushujuji",
"unicode": "e9dd",
"unicode_decimal": 59869
},
{
"icon_id": "47470095",
"name": "语音-1",
"font_class": "yuyin-1",
"unicode": "e9de",
"unicode_decimal": 59870
},
{
"icon_id": "47470094",
"name": "图像 -1",
"font_class": "a-tuxiang-1",
"unicode": "e9e0",
"unicode_decimal": 59872
},
{
"icon_id": "47470092",
"name": "下载",
"font_class": "xiazai3",
"unicode": "e9e1",
"unicode_decimal": 59873
},
{
"icon_id": "47464883",
"name": "时间",
"font_class": "shijian1",
"unicode": "e9cb",
"unicode_decimal": 59851
},
{
"icon_id": "47464882",
"name": "浏览量",
"font_class": "liulanliang",
"unicode": "e9cc",
"unicode_decimal": 59852
},
{
"icon_id": "47337195",
"name": "关闭",
"font_class": "guanbi2",
"unicode": "e9ca",
"unicode_decimal": 59850
},
{
"icon_id": "47202525",
"name": "展示上线",
"font_class": "zhanshishangxian1",
"unicode": "e9d3",
"unicode_decimal": 59859
},
{
"icon_id": "47202524",
"name": "填写信息",
"font_class": "tianxiexinxi",
"unicode": "e9d8",
"unicode_decimal": 59864
},
{
"icon_id": "47202523",
"name": "提交申请",
"font_class": "tijiaoshenqing1",
"unicode": "e9d9",
"unicode_decimal": 59865
},
{
"icon_id": "47202522",
"name": "管理员审核",
"font_class": "guanliyuanshenhe1",
"unicode": "e9da",
"unicode_decimal": 59866
},
{
"icon_id": "47195847",
"name": "私信",
"font_class": "sixin2",
"unicode": "e9e3",
"unicode_decimal": 59875
},
{
"icon_id": "47195594",
"name": "点赞",
"font_class": "dianzan4",
"unicode": "e9d6",
"unicode_decimal": 59862
},
{
"icon_id": "47195591",
"name": "浏览数",
"font_class": "liulanshu",
"unicode": "e9d7",
"unicode_decimal": 59863
},
{
"icon_id": "47195601",
"name": "帖子数",
"font_class": "tiezishu",
"unicode": "e9ce",
"unicode_decimal": 59854
},
{
"icon_id": "47195600",
"name": "研究领域",
"font_class": "yanjiulingyu",
"unicode": "e9cf",
"unicode_decimal": 59855
},
{
"icon_id": "47195597",
"name": "修改 (3)",
"font_class": "a-xiugai3",
"unicode": "e9d0",
"unicode_decimal": 59856
},
{
"icon_id": "47195598",
"name": "已提交的入驻信息",
"font_class": "yitijiaoderuzhuxinxi",
"unicode": "e9d1",
"unicode_decimal": 59857
},
{
"icon_id": "47195599",
"name": "私信",
"font_class": "sixin",
"unicode": "e9d2",
"unicode_decimal": 59858
},
{
"icon_id": "47195593",
"name": "个人简介",
"font_class": "gerenjianjie",
"unicode": "e9d4",
"unicode_decimal": 59860
},
{
"icon_id": "47195595",
"name": "关注",
"font_class": "guanzhu1",
"unicode": "e9d5",
"unicode_decimal": 59861
},
{
"icon_id": "47139445",
"name": "我也发帖",
"font_class": "woyefatie",
"unicode": "e9c9",
"unicode_decimal": 59849
},
{
"icon_id": "46808654",
"name": "应用模板",
"font_class": "yingyongmoban",
"unicode": "e9c8",
"unicode_decimal": 59848
},
{
"icon_id": "46593376",
"name": "状态",
"font_class": "zhuangtai",
"unicode": "e9c5",
"unicode_decimal": 59845
},
{
"icon_id": "46593375",
"name": "复制-删除",
"font_class": "fuzhi-shanchu1",
"unicode": "e9c6",
"unicode_decimal": 59846
},
{
"icon_id": "46593374",
"name": "模版管理",
"font_class": "mobanguanli",
"unicode": "e9c7",
"unicode_decimal": 59847
},
{
"icon_id": "46370299",
"name": "svg_箭头1",
"font_class": "svg_jiantou1",
"unicode": "e9c3",
"unicode_decimal": 59843
},
{
"icon_id": "46370300",
"name": "左箭头",
"font_class": "zuojiantou1",
"unicode": "e9c4",
"unicode_decimal": 59844
},
{
"icon_id": "46329189",
"name": "箭头",
"font_class": "jiantou4",
"unicode": "e9c2",
"unicode_decimal": 59842
},
{
"icon_id": "46248622",
"name": "合作伙伴",

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,39 +0,0 @@
@font-face {
font-family: "iconfont"; /* Project id 5081288 */
src: url('iconfont.woff2?t=1764899187997') format('woff2'),
url('iconfont.woff?t=1764899187997') format('woff'),
url('iconfont.ttf?t=1764899187997') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-icon_blackarrow_hover:before {
content: "\e78f";
}
.icon-banben:before {
content: "\e7a1";
}
.icon-shoucang:before {
content: "\e7a0";
}
.icon-liulanliang:before {
content: "\e79e";
}
.icon-gongju:before {
content: "\e79f";
}
.icon-xiangmu:before {
content: "\e79d";
}

File diff suppressed because one or more lines are too long

View File

@ -1,51 +0,0 @@
{
"id": "5081288",
"name": "软件工厂专区",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "46273104",
"name": "icon_blackarrow_hover",
"font_class": "icon_blackarrow_hover",
"unicode": "e78f",
"unicode_decimal": 59279
},
{
"icon_id": "46258083",
"name": "版本",
"font_class": "banben",
"unicode": "e7a1",
"unicode_decimal": 59297
},
{
"icon_id": "46258084",
"name": "收藏",
"font_class": "shoucang",
"unicode": "e7a0",
"unicode_decimal": 59296
},
{
"icon_id": "46258085",
"name": "浏览量",
"font_class": "liulanliang",
"unicode": "e79e",
"unicode_decimal": 59294
},
{
"icon_id": "46258086",
"name": "工具",
"font_class": "gongju",
"unicode": "e79f",
"unicode_decimal": 59295
},
{
"icon_id": "46258087",
"name": "项目",
"font_class": "xiangmu",
"unicode": "e79d",
"unicode_decimal": 59293
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,539 +0,0 @@
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View File

@ -1,101 +0,0 @@
@font-face {
font-family: "iconfont-color"; /* Project id 5169666 */
/* Color fonts */
src:
url('iconfont.woff2?t=1778741571506') format('woff2'),
url('iconfont.woff?t=1778741571506') format('woff'),
url('iconfont.ttf?t=1778741571506') format('truetype');
}
.iconfont-color {
font-family: "iconfont-color" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-colorqita:before {
content: "\e7b3";
}
.icon-coloryidongkaifa:before {
content: "\e7b4";
}
.icon-colordaimazhijian:before {
content: "\e7b5";
}
.icon-coloryunxiaogongju:before {
content: "\e7b6";
}
.icon-coloryouxiyule:before {
content: "\e7b7";
}
.icon-colorskill:before {
content: "\e7b0";
}
.icon-colorshujuji:before {
content: "\e7b1";
}
.icon-colordaimaku1:before {
content: "\e7b2";
}
.icon-colormoxingku:before {
content: "\e7af";
}
.icon-coloryinpin:before {
content: "\e7ad";
}
.icon-colorremenmoxing-1:before {
content: "\e7a3";
}
.icon-colorkexueyanjiuhejishufuwuye:before {
content: "\e7ae";
}
.icon-colora-zu18210:before {
content: "\e7a6";
}
.icon-colora-zu18240:before {
content: "\e7a5";
}
.icon-colora-zuixin1:before {
content: "\e7a7";
}
.icon-colorziranyuyanchuli:before {
content: "\e7a8";
}
.icon-colorshichangtuiguang-01:before {
content: "\e7a4";
}
.icon-coloryuyin:before {
content: "\e7a9";
}
.icon-colortuxiang:before {
content: "\e7aa";
}
.icon-colora-zu18213:before {
content: "\e7ab";
}
.icon-colorshipinbofang:before {
content: "\e7ac";
}

File diff suppressed because one or more lines are too long

View File

@ -1,156 +0,0 @@
{
"id": "5169666",
"name": "newForge-color",
"font_family": "iconfont-color",
"css_prefix_text": "icon-color",
"description": "gitlink/红山开源 带颜色的icon",
"glyphs": [
{
"icon_id": "47487873",
"name": "其他",
"font_class": "qita",
"unicode": "e7b3",
"unicode_decimal": 59315
},
{
"icon_id": "47487872",
"name": "移动开发",
"font_class": "yidongkaifa",
"unicode": "e7b4",
"unicode_decimal": 59316
},
{
"icon_id": "47487870",
"name": "代码质检",
"font_class": "daimazhijian",
"unicode": "e7b5",
"unicode_decimal": 59317
},
{
"icon_id": "47487871",
"name": "云效工具",
"font_class": "yunxiaogongju",
"unicode": "e7b6",
"unicode_decimal": 59318
},
{
"icon_id": "47487869",
"name": "游戏娱乐",
"font_class": "youxiyule",
"unicode": "e7b7",
"unicode_decimal": 59319
},
{
"icon_id": "47487614",
"name": "skill",
"font_class": "skill",
"unicode": "e7b0",
"unicode_decimal": 59312
},
{
"icon_id": "47487615",
"name": "数据集",
"font_class": "shujuji",
"unicode": "e7b1",
"unicode_decimal": 59313
},
{
"icon_id": "47487613",
"name": "代码库",
"font_class": "daimaku1",
"unicode": "e7b2",
"unicode_decimal": 59314
},
{
"icon_id": "47487578",
"name": "模型库",
"font_class": "moxingku",
"unicode": "e7af",
"unicode_decimal": 59311
},
{
"icon_id": "47470053",
"name": "音频",
"font_class": "yinpin",
"unicode": "e7ad",
"unicode_decimal": 59309
},
{
"icon_id": "47470038",
"name": "热门模型-1",
"font_class": "remenmoxing-1",
"unicode": "e7a3",
"unicode_decimal": 59299
},
{
"icon_id": "47470036",
"name": "科学研究和技术服务业",
"font_class": "kexueyanjiuhejishufuwuye",
"unicode": "e7ae",
"unicode_decimal": 59310
},
{
"icon_id": "47470068",
"name": "组 18210",
"font_class": "a-zu18210",
"unicode": "e7a6",
"unicode_decimal": 59302
},
{
"icon_id": "47470067",
"name": "组 18240",
"font_class": "a-zu18240",
"unicode": "e7a5",
"unicode_decimal": 59301
},
{
"icon_id": "47470066",
"name": "最新 (1)",
"font_class": "a-zuixin1",
"unicode": "e7a7",
"unicode_decimal": 59303
},
{
"icon_id": "47470060",
"name": "自然语言处理",
"font_class": "ziranyuyanchuli",
"unicode": "e7a8",
"unicode_decimal": 59304
},
{
"icon_id": "47470052",
"name": "市场推广-01",
"font_class": "shichangtuiguang-01",
"unicode": "e7a4",
"unicode_decimal": 59300
},
{
"icon_id": "47470056",
"name": "语音",
"font_class": "yuyin",
"unicode": "e7a9",
"unicode_decimal": 59305
},
{
"icon_id": "47470055",
"name": "图像",
"font_class": "tuxiang",
"unicode": "e7aa",
"unicode_decimal": 59306
},
{
"icon_id": "47470057",
"name": "组 18213",
"font_class": "a-zu18213",
"unicode": "e7ab",
"unicode_decimal": 59307
},
{
"icon_id": "47470047",
"name": "视频播放",
"font_class": "shipinbofang",
"unicode": "e7ac",
"unicode_decimal": 59308
}
]
}

Binary file not shown.

Binary file not shown.

View File

@ -2,21 +2,21 @@
<html lang="zh-hans-CN" class="notranslate translated-ltr" translate="no">
<head>
<title>菌队可控开源社区</title>
<title>生态治理平台</title>
<meta charset="utf-8">
<meta name="Keywords" content="菌队可控开源社区, git, 开源, 代码托管, 项目管理, 版本控制, 代码分享, 项目协作, 协同开发平台">
<meta name="description" content="菌队可控开源社区 是一个新一代的开源创新服务平台,提供分布式协作开发、流水线运维、代码分析等功能,基于 Git 打造分布式代码托管环境。">
<meta property="og:title" content="菌队可控开源社区" />
<meta name="Keywords" content="生态治理平台, git, 开源, 代码托管, 项目管理, 版本控制, 代码分享, 项目协作, 协同开发平台">
<meta name="description" content="生态治理平台 是一个新一代的开源创新服务平台,提供分布式协作开发、流水线运维、代码分析等功能,基于 Git 打造分布式代码托管环境。">
<meta property="og:title" content="生态治理平台" />
<meta property="og:type" content="website" />
<meta property="og:image:alt" content="菌队可控开源社区" />
<meta property="og:site_name" content="菌队可控开源社区" />
<meta property="og:image:alt" content="生态治理平台" />
<meta property="og:site_name" content="生态治理平台" />
<meta property="og:url" content="https://gitlink.org.cn/" />
<meta property="og:image" content="https://www.gitlink.org.cn/images/logo.png" />
<meta property="og:description" content="菌队可控开源社区 是一个新一代的开源创新服务平台,提供分布式协作开发、流水线运维、代码分析等功能,基于 Git 打造分布式代码托管环境。" />
<meta property="og:description" content="生态治理平台 是一个新一代的开源创新服务平台,提供分布式协作开发、流水线运维、代码分析等功能,基于 Git 打造分布式代码托管环境。" />
<meta name="keyboard-shortcuts-preference" content="all">
<meta name="octolytics-dimension-user_login" content="菌队可控开源社区"></meta>
<meta name="octolytics-dimension-repository_nwo" content="菌队可控开源社区"></meta>
<meta name="octolytics-dimension-repository_network_root_nwo" content="菌队可控开源社区"></meta>
<meta name="octolytics-dimension-user_login" content="生态治理平台"></meta>
<meta name="octolytics-dimension-repository_nwo" content="生态治理平台"></meta>
<meta name="octolytics-dimension-repository_network_root_nwo" content="生态治理平台"></meta>
<meta name="theme-color" content="#000000">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="referrer" content="always">
@ -27,22 +27,21 @@
<!-- 按需引用viewport content -->
<meta name="viewport" content="">
<meta name="keyboard-shortcuts-preference" content="all">
<meta name="octolytics-dimension-user_login" content="菌队可控开源社区"></meta>
<meta name="octolytics-dimension-repository_nwo" content="菌队可控开源社区"></meta>
<meta name="octolytics-dimension-repository_network_root_nwo" content="菌队可控开源社区"></meta>
<meta name="octolytics-dimension-user_login" content="生态治理平台"></meta>
<meta name="octolytics-dimension-repository_nwo" content="生态治理平台"></meta>
<meta name="octolytics-dimension-repository_network_root_nwo" content="生态治理平台"></meta>
<meta name="theme-color" content="#000000">
<meta content="IE=edge" http-equiv="X-UA-Compatible">
<meta name="twitter:site" content="@gitlink">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="菌队可控开源社区">
<meta name="twitter:description" content="菌队可控开源社区 开源创新服务平台 提供分布式协作开发 流水线运维 代码分析等功能 基于 Git 打造分布式代码托管环境">
<meta name="twitter:title" content="生态治理平台">
<meta name="twitter:description" content="生态治理平台 开源创新服务平台 提供分布式协作开发 流水线运维 代码分析等功能 基于 Git 打造分布式代码托管环境">
<meta content="always" name="referrer">
<link rel="canonical" href="https://gitlink.org.cn" data-turbo-transient="">
<link rel="fluid-icon" href="https://www.gitlink.org.cn/images/logo.png" title="GitLink">
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/alex/alex.all.global.min.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont.css">
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/iconfont_color/iconfont.css">
<!-- <link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/edu-purge.css"> -->
<link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/editormd.min.css">
<!-- <link rel="stylesheet" type="text/css" href="%PUBLIC_URL%css/merge.css"> -->

View File

@ -146,74 +146,3 @@ body {
min-height: calc(100% - 60px) !important;
}
}
.width1600 {
width: 1600px;
margin: 0 auto;
}
.opacity9 {
opacity: 0.9;
}
.opacity8 {
opacity: 0.8;
}
.opacity7 {
opacity: 0.7;
}
.opacity6 {
opacity: 0.6;
}
.opacity4 {
opacity: 0.4;
}
.mt-10{
margin-top: -10px;
}
.df-center-between{
display: flex;
justify-content: space-between;
align-items: center;
}
.df-center{
display: flex;
align-items: center;
}
.df-between{
display: flex;
justify-content: space-between;
}
.df-start{
display: flex;
align-items: flex-start;
}
.grid-item-top {
display: grid;
align-items: center;
grid-template-columns: max-content 1fr;
align-items: flex-start;
}
@media screen and (max-width: 1366px) {
body{
zoom: 0.75;
}
.eco_home_banner{
height: calc(100vh * 1.85) !important;
}
body .fullscreen-element{
width: 133vw;
height: 135vh;
}
body .news .header-content, body .manual, body .news-detail .news-detail-header .ant-breadcrumb, body .version .manual{
padding: 0 calc((133vw - 1600px) / 2);
}
body .news .header-right{
width: 53vw;
}
}

View File

@ -49,65 +49,9 @@ const Information = Loadable({
loading: Loading,
})
const SoftwareFactory = Loadable({
loader: () => import('./factory/index'),
loader: () => import('./forge/SoftwareFactory/index'),
loading: Loading,
})
const SoftwareFactoryResource = Loadable({
loader: () => import('./factory/resource/index'),
loading: Loading,
})
const SoftwareFactoryResourceDetail= Loadable({
loader: () => import('./factory/resource/developmentTools/detail/index'),
loading: Loading,
})
const SoftwareFactoryResourceDetailCommon= Loadable({
loader: () => import('./factory/resource/commonTools/detail/index'),
loading: Loading,
})
const SoftwareFactoryVersionDetail = Loadable({
loader: () => import('./factory/resource/factoryVersion/detail/index'),
loading: Loading,
})
const SoftwareFactoryNews = Loadable({
loader: () => import('./factory/news/index'),
loading: Loading,
})
const SoftwareFactoryNewsDetail= Loadable({
loader: () => import('./factory/news/detail/index'),
loading: Loading,
})
const SoftwareFactoryLessons = Loadable({
loader: () => import('./factory/lessons/index'),
loading: Loading,
})
const SoftwareFactoryLessonsDetail = Loadable({
loader: () => import('./factory/lessons/detail/index'),
loading: Loading,
})
const SoftwareFactoryScene = Loadable({
loader: () => import('./factory/scene/index'),
loading: Loading,
})
// 反馈中心页面
const FeedbackCenter = Loadable({
loader: () => import('./factory/feedback/index'),
loading: Loading,
})
//forge安全设置
const Security = Loadable({
loader: () => import('./forge/SecuritySetting/Index'),
@ -165,12 +109,12 @@ const ProjectIndex = Loadable({
});
const Home = Loadable({
loader: () => import('./forge/Main/projecthome/v2/index'),
loader: () => import('./forge/Main/projecthome/Index'),
loading: Loading,
})
const Store = Loadable({
loader: () => import('./forge/Main/projecthome/Index'),
loader: () => import('./forge/Main/projectHomeNew/v2'),
loading: Loading,
})
@ -185,12 +129,12 @@ const LoginRegisterPage = Loadable({
const AboutUs = Loadable({
loader: () => import("./forge/AboutUs/AboutUs"),
loading: Loading,
loader: () => import("./forge/AboutUs/AboutUs"),
loading: Loading,
})
const Teaching = Loadable({
loader: () => import("./forge/TeachingPractice/Teaching"),
loader : () => import("./forge/TeachingPractice/Teaching"),
loading: Loading,
})
@ -203,7 +147,7 @@ const ConfigurateBot = Loadable({
loading: Loading,
});
const SoftBot = Loadable({
loader: () => import("./softbot"),
loader : () => import("./softbot"),
loading: Loading,
})
@ -236,28 +180,8 @@ const GovernanceIframe = Loadable({
loading: Loading,
});
const OssRepoIframe = Loadable({
loader: () => import("./forge/ossRepoIframe"),
loading: Loading,
});
const Forums = Loadable({
loader: () => import("./forums"),
loading: Loading,
});
const News = Loadable({
loader: () => import("./news"),
loading: Loading,
});
const AI = Loadable({
loader: () => import("./ai"),
loading: Loading,
});
// 此处仅维护前端可能的一级路由,不用进行项目或者组织判断的字段。
const keyWord = ["ossRepo", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot","zone", "hiagent","sf", "development", "governance", "explore","forums", "news", "exploreStore", "factory", "zoneAdmin", "ai"];
const keyWord = ["explore", "settings", "setting", "mulan", "wiki", "issues", "setting", "trending", "code", "projects", "pulls", "mine", "login", "register", "email", "export", "nopage", "404", "403", "500", "501", "search", "organize", "login", "register", "resetPassword", "aboutus","educoder", "glcc","bindlogin", "softbot","zone", "hiagent","softwareFactory", "development", "governance"];
class App extends Component {
constructor(props) {
@ -280,17 +204,17 @@ class App extends Component {
const login = cookie.load("login");
let path = this.props.history.location.pathname;
if (login && (path === "/login" || path === "/register" || path === "/resetPassword" || path.indexOf("/bindlogin") > -1)) {
if(login && (path === "/login" || path === "/register" || path === "/resetPassword" || path.indexOf("/bindlogin") >-1 )){
this.props.history.push(`/${login}`);
}
// 添加路由监听,决定组织还是个人
this.unlisten = this.props.history.listen((location, action) => {
this.unlisten = this.props.history.listen((location,action) => {
let newPathname = location.pathname.split('/')[1];
if (location.pathname === '/' && action === 'POP') { // 浏览器回退/前进
this.setState({
pathName: null,
pathType: null
});
});
return;
}
if (newPathname !== this.state.pathName) {
@ -300,7 +224,7 @@ class App extends Component {
}
shouldComponentUpdate(nextProps, nextState) {
if (!nextProps.location.pathname === '/') {
if(!nextProps.location.pathname==='/'){
return true;
}
// 阻止个人与组织切换时组件渲染请求导致的404
@ -322,7 +246,7 @@ class App extends Component {
})
}
});
} else {
}else{
this.setState({
pathType: pathname,
pathName: pathname,
@ -430,35 +354,18 @@ class App extends Component {
render() {
const { pathType, pathName, mygetHelmetapi } = this.state;
return (
<ConfigProvider locale={zhCN}>
<MuiThemeProvider theme={theme}>
<LoginDialog {...this.props} {...this.state} Modifyloginvalue={() => this.Modifyloginvalue()}></LoginDialog>
{/* <GlccModal /> */}
{/* {!pathName || (pathName && pathName.toLowerCase() !== 'glcc') ? <SiderBar {...this.props}/> : <SiderBarHelp/>} */}
{pathName && pathName.toLowerCase() === 'glcc' && <SiderBarHelp />}
{pathName && pathName.toLowerCase() === 'glcc' && <SiderBarHelp/>}
{/* <Router> */}
{
pathType ?
<Switch>
<Route path="/ai"
render={
(props) => (<AI {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 社区动态页面 */}
<Route path="/news"
render={
(props) => (<News {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 论坛交流页面 */}
<Route path="/forums"
render={
(props) => (<Forums {...this.props} {...props} {...this.state} />)
}
></Route>
{/* iframe页面 */}
<Route path="/hiagent"
render={
@ -472,222 +379,141 @@ class App extends Component {
}
} />
{/* svg */}
<Route path="/:owner/:projectsId/badge/:type" render={
(props) => {
return (<Badge {...this.props} {...props} {...this.state} />)
}
} />
{/* svg */}
<Route path="/:owner/:projectsId/badge/:type" render={
(props) => {
return (<Badge {...this.props} {...props} {...this.state} />)
}
} />
{/* webIDE */}
{/* <Route path="/:owner/:projectsId/webIDE/tree/:branchName"
{/* webIDE */}
{/* <Route path="/:owner/:projectsId/webIDE/tree/:branchName"
render={
(props) => (<WebIDE {...this.props} {...props} {...this.state} />)
}
></Route> */}
{/* 项目PR */}
<Route path="/:owner/:projectsId/compare"
render={
(props) => (<ProjectDetail {...this.props} {...props} {...this.state} />)
}
></Route>
{/* 项目PR */}
<Route path="/:owner/:projectsId/compare"
render={
(props) => (<ProjectDetail {...this.props} {...props} {...this.state} />)
}
></Route>
{/*项目*/}
<Route
path={"/:owner/:projectId/devops/:opsId/detail"}
render={
(props) => {
return (<OpsDetail {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/* 上架bot配置 */}
<Route
path="/settings/mybot/configuration/:id"
render={(p) => (
<ConfigurateBot {...this.props} {...p} />
)}
></Route>
<Route
path={"/zone/:deptId"}
render={
(props) => {
return (<Information {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/*项目*/}
<Route
path={"/:owner/:projectId/devops/:opsId/detail"}
render={
(props) => {
return (<OpsDetail {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/* 上架bot配置 */}
<Route
path="/settings/mybot/configuration/:id"
render={(p) => (
<ConfigurateBot {...this.props} {...p}/>
)}
></Route>
<Route
path={"/zone/:deptId"}
render={
(props) => {
return (<Information {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/softwareFactory/home"}
render={
(props) => {
return (<SoftwareFactory {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/settings"}
render={
(props) => {
return (<Security {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/*403*/}
<Route path="/403" component={Shixunauthority} />
<Route
path={"/sf/resources/devtools/:id"}
render={
(props) => {
return (<SoftwareFactoryResourceDetail {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route path="/500" component={http500} />
<Route
path={"/sf/resources/commontools/:id"}
render={
(props) => {
return (<SoftwareFactoryResourceDetailCommon {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/*404*/}
<Route path="/nopage" component={Shixunnopage} />
<Route path="/development" component={Development} />
<Route
path={"/sf/resources/releases/:id"}
render={
(props) => {
return (<SoftwareFactoryVersionDetail {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/* 查询 */}
<Route path="/search" component={Search} mygetHelmetapi={mygetHelmetapi}/>
<Route
path={"/sf/resources"}
render={
(props) => {
return (<SoftwareFactoryResource {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/* 项目标签列表页 */}
<Route path="/explore/topic/:id/:name" component={Topic} mygetHelmetapi={mygetHelmetapi}/>
<Route
path={"/sf/news/:id"}
render={
(props) => {
return (<SoftwareFactoryNewsDetail {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route exact path="/explore/all"
render={
(props) => (
<ProjectIndex {...this.props} {...props} />
)
}
/>
<Route exact path="/explore"
render={
(props) => (
<Store {...this.props} {...props} />
)
}
/>
<Route path={"/:OIdentifier/enterprise"}
render={
(props) => {
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
}
}>
</Route>
{/* 关联账号 */}
<Route
path="/bindlogin/:type"
render={(props) =><Relaction {...this.props} {...props} mygetHelmetapi={mygetHelmetapi}/>}
></Route>
<Route
path={"/sf/news"}
render={
(props) => {
return (<SoftwareFactoryNews {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/sf/lessons/:id"}
render={
(props) => {
return (<SoftwareFactoryLessonsDetail {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/sf/lessons"}
render={
(props) => {
return (<SoftwareFactoryLessons {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/* 登录 */}
<Route
path="/login"
render={(props) =><LoginRegisterPage {...this.props} {...props} mygetHelmetapi={mygetHelmetapi}/>}
></Route>
{/* 注册 */}
<Route
path="/register"
render={(props) =><LoginRegisterPage {...this.props} {...props} mygetHelmetapi={mygetHelmetapi}/>}
></Route>
{/* 忘记密码 */}
<Route
path="/resetPassword"
render={(props) =><LoginRegisterPage {...this.props} {...props} mygetHelmetapi={mygetHelmetapi}/>}
></Route>
{/* 关于我们 */}
<Route
path = "/aboutus"
render = {(props)=><AboutUs {...this.props} {...props}/>}
></Route>
<Route
path={"/sf/scene/:sceneId"}
render={
(props) => {
return (<SoftwareFactoryScene {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/* 教学实践 */}
<Route
path="/educoder"
render={(props) => <Teaching {...this.props} {...props} />}
></Route>
{/* 反馈中心页面 */}
<Route
path={"/sf/feedback"}
render={
(props) => {
return (<FeedbackCenter {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/sf"}
render={
(props) => {
return (<SoftwareFactory {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/settings"}
render={
(props) => {
return (<Security {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/*403*/}
<Route path="/403" component={Shixunauthority} />
<Route path="/500" component={http500} />
{/*404*/}
<Route path="/nopage" component={Shixunnopage} />
<Route path="/development" component={Development} />
{/* 查询 */}
<Route path="/search" component={Search} mygetHelmetapi={mygetHelmetapi} />
{/* 项目标签列表页 */}
<Route path="/ossRepo/topic/:id/:name" component={Topic} mygetHelmetapi={mygetHelmetapi} />
<Route path={"/:OIdentifier/enterprise"}
render={
(props) => {
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
}
}>
</Route>
{/* 关联账号 */}
<Route
path="/bindlogin/:type"
render={(props) => <Relaction {...this.props} {...props} mygetHelmetapi={mygetHelmetapi} />}
></Route>
{/* 登录 */}
<Route
path="/login"
render={(props) => <LoginRegisterPage {...this.props} {...props} mygetHelmetapi={mygetHelmetapi} />}
></Route>
{/* 注册 */}
<Route
path="/register"
render={(props) => <LoginRegisterPage {...this.props} {...props} mygetHelmetapi={mygetHelmetapi} />}
></Route>
{/* 忘记密码 */}
<Route
path="/resetPassword"
render={(props) => <LoginRegisterPage {...this.props} {...props} mygetHelmetapi={mygetHelmetapi} />}
></Route>
{/* 关于我们 */}
<Route
path="/aboutus"
render={(props) => <AboutUs {...this.props} {...props} />}
></Route>
{/* 教学实践 */}
<Route
path="/educoder"
render={(props) => <Teaching {...this.props} {...props} />}
></Route>
{/*glcc */}
{/* <Route
{/*glcc */}
{/* <Route
path="/glcc/:id"
render={(props) => <Glcc {...this.props} {...props} />}
></Route>
@ -696,117 +522,80 @@ class App extends Component {
render={(props) => <Glcc {...this.props} {...props} />}
></Route> */}
{/* soft市场页 */}
<Route
path="/softbot"
render={(props) => (
<SoftBot {...this.props} {...props} />
)}
></Route>
{/* soft市场页 */}
<Route
path="/softbot"
render={(props) => (
<SoftBot {...this.props} {...props} />
)}
></Route>
{/* 组织 */}
<Route path={"/organize"}
render={
(props) => {
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
}
}>
</Route>
{/*新建项目等*/}
<Route
path={"/projects"}
render={
(props) => {
return (<Projects {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path="/exploreStore"
render={(props) => (
<Projects {...this.props} {...props} />
)}
></Route>
<Route exact path="/ossRepo"
render={
(props) => (
<Projects {...this.props} {...props} {...this.state}/>
)
}
/>
<Route
path="/eco"
render={(props) => (
<Store {...this.props} {...props} />
)}
></Route>
<Route
path={"/governance"}
render={
(props) => {
return (<GovernanceIframe {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/explore"}
render={
(props) => {
return (<OssRepoIframe {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/factory"}
render={
(props) => {
return (<Home {...this.props} {...props} {...this.state} />)
}
}>
</Route>
{/* 判断为用户/组织,并进入对应页面 */}
{
pathType === 'User' ?
<Route exact path="/:username"
render={
(props) => {
return (<InfosIndex {...this.props} {...this.state} />)
}
}
/> : pathType === 'Organization' ? <Route path={"/:OIdentifier"}
render={
(props) => {
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
}
}>
</Route> : pathType === '404' ? <Route component={Shixunnopage} /> :
""
{/* 组织 */}
<Route path={"/organize"}
render={
(props) => {
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
}
{/* 个人主页 */}
<Route path="/:username"
render={
(props) => {
return (<InfosIndex {...this.props} {...this.state} />)
}
}></Route>
}>
</Route>
{/*新建项目等*/}
<Route
path={"/projects"}
render={
(props) => {
return (<Projects {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route
path={"/governance"}
render={
(props) => {
return (<GovernanceIframe {...this.props} {...props} {...this.state} />)
}
}>
</Route>
<Route component={Shixunnopage} />
</Switch>
:
<Route exact path="/"
{/* 判断为用户/组织,并进入对应页面 */}
{
pathType === 'User' ?
<Route exact path="/:username"
render={
(props) => (
<Home {...props} {...this.props} {...this.state} />
)
(props) => {
return (<InfosIndex {...this.props} {...this.state} />)
}
}
/>
}
/> : pathType === 'Organization' ? <Route path={"/:OIdentifier"}
render={
(props) => {
return (<OrganizeIndex {...props} {...this.props} {...this.state} />)
}
}>
</Route> : pathType === '404' ? <Route component={Shixunnopage} />:
""
}
{/* 个人主页 */}
<Route path="/:username"
render={
(props) => {
return (<InfosIndex {...this.props} {...this.state} />)
}
}></Route>
<Route component={Shixunnopage} />
</Switch>
:
<Route exact path="/"
render={
(props) => (
<Home {...props} {...this.props} {...this.state}/>
)
}
/>
}
</MuiThemeProvider>
</ConfigProvider>
);

View File

@ -3,7 +3,7 @@ import { requestProxy } from "./indexEduplus2RequestProxy";
import { broadcastChannelOnmessage, isDev, queryString } from 'educoder';
import { notification } from 'antd';
import './index.scss';
import './index.css';
let message501 = false;
broadcastChannelOnmessage('refreshPage', () => {

View File

@ -1,29 +0,0 @@
import fetch from './fetch';
import fetchLogin from './fetchLogin';
/**智能体分类列表***/
export function getAgentTypeList(params) {
return fetch({
url: `/console-api/home-page/agent-square/get-bot-type-list`,
method: 'get',
params
});
}
/**智能体列表***/
export function getAgentList(params) {
return fetch({
url: `/console-api/home-page/agent-square/get-bot-page-by-type`,
method: 'get',
params
});
}
/**智能体登录***/
export function agentLogin(data) {
return fetchLogin({
url: `/api/login/oauth/access_token`,
method: 'post',
data
});
}

View File

@ -1,48 +0,0 @@
import { message, Modal } from "antd"
import { deleteProjectInZoneById, deleteSourceById, takeDownSourceById } from "../forge/Information/api";
import { useState } from "react";
export const DeleteModal = ({type, id, cancel, roleByZone, reload}) =>{
const [loading, setLoading] = useState(false)
return <Modal className="purpleGradient" title="删除资源" visible={!!id} onCancel={cancel} okButtonProps={{loading: loading}} onOk={()=>{
const deleteFn = type === "codes" ? deleteProjectInZoneById : deleteSourceById;
setLoading(true);
deleteFn(id, roleByZone).then(res=>{
if(res && res.data && res.data.code === 200){
message.success(`操作成功`);
cancel();
reload(Math.random());
}
}).finally(()=>{
setLoading(false);
})
}}>
<div className="center pt20">
<i className="iconfont icon-shanchu_tc_icon color-red mr10"></i>
<span className="font-18">确定删除资源吗</span>
<div className="mt20 mb40">该操作不可恢复请确认后再操作</div>
</div>
</Modal>
}
export const TakeDownModal = ({id, cancel, roleByZone, reload}) =>{
const [loading, setLoading] = useState(false)
return <Modal key="down" className="purpleGradient" title="下架资源" visible={!!id} onCancel={cancel} okButtonProps={{loading: loading}} onOk={()=>{
setLoading(true);
takeDownSourceById(id, roleByZone).then(res=>{
if(res && res.data && res.data.code === 200){
message.success(`操作成功`);
cancel();
reload(Math.random());
}
}).finally(()=>{
setLoading(false);
})
}}>
<div className="center pt20">
<i className="iconfont icon-shanchu_tc_icon color-red mr10"></i>
<span className="font-18">确定下架资源吗</span>
<div className="mt20 mb40">下架后AI资源广场将隐藏该资源但可重新申请上架</div>
</div>
</Modal>
}

View File

@ -1,560 +0,0 @@
import React, { useState, useEffect, Fragment, useRef } from "react";
import { Input, Select, Upload, message, Form, Checkbox, Tree, Spin, Alert } from "antd";
import './index.scss';
import { aiCreateSteps, aiResouceType, aiResouceTypeKey, convertToTreeData } from "../static";
import { Link } from "react-router-dom";
import { AlignCenter } from "../../forge/Component/layout";
import DorpChooseMenus from "../../forge/Component/licenseModal/dorpChooseMenus";
import axios from "axios";
import { addDataset, addModel, addProjectInZone, addSkill, editDataset, editModel, editSkill, getProjectsTypeLists, getAISourceById, getSourceZoneList, parseFileBySkill } from "../../forge/Information/api";
import ChunkedUpload from "../../components/ChunkedUpload";
const { Option } = Select;
function CreateResource(props) {
const { match: { params }, form, current_user, history, aiZoneInfo, roleByZone } = props || {};
const { getFieldDecorator, getFieldsValue, setFieldsValue, validateFields } = form;
const type = params.type || aiResouceType[0].key;
const sourceId = params.id; //id
const { user_id, login } = current_user || {}
const { id: aiZoneId } = aiZoneInfo || {}
const [loading, setLoading] = useState(false);
const [currentStep, setCurrentStep] = useState(0);
const [categoryList, setCategoryList] = useState();
const [languageList, setLanguageList] = useState();
const [gitignoreList, setGitignoreList] = useState();
const [licensesList, setLicensesList] = useState();
const [ownerList, setOwnerList] = useState();
const [cate, setCate] = useState();
const [info, setInfo] = useState();
const [uploadedFile, setUploadedFile] = useState(null);
const [remark, setRemark] = useState('');
const uploadRef = useRef(null);
const typeDetail = type && aiResouceType.find(i => i.key === type);
const { ignoreFlag, licenseFlag, categoreFlag, languageFlag, file } = getFieldsValue();
const { file: fileByupload } = file || {}
const { response: responseByupload } = fileByupload || {}
const { fileTreeJson } = responseByupload || {}
const skillTreeData = fileTreeJson && convertToTreeData(JSON.parse(fileTreeJson).children)
//
useEffect(() => {
if (!login) {
history.push(`/login?go_page=${encodeURIComponent(window.location.pathname)}`);
}
}, [login]);
// 5
useEffect(() => {
document.title = `发布新资源`;
const timer = setInterval(() => {
setCurrentStep((prev) => (prev + 1) % aiCreateSteps.length);
}, 5000);
return () => clearInterval(timer);
}, []);
useEffect(() => {
switch (type) {
case "codes":
axios.get(`/owners.json`).then(result => {
if (result && result.data && result.data.owners) {
setOwnerList(result.data.owners)
const owner = result.data.owners.filter(item => item.id === user_id);
owner[0] && setTimeout(() => {
setFieldsValue({
user_id: owner[0].id
})
}, 500);
}
})
axios.get(`/project_categories.json`).then((result) => {
if (result && result.data) {
setCategoryList(result.data.project_categories)
}
})
axios.get(`/project_languages.json`).then((result) => {
if (result && result.data) {
setLanguageList(result.data.project_languages)
}
})
axios.get(`/ignores.json`).then((result) => {
if (result && result.data) {
setGitignoreList(result.data.ignores)
}
})
axios.get(`/licenses.json`).then((result) => {
if (result && result.data) {
setLicensesList(result.data.licenses)
}
})
aiZoneId && getProjectsTypeLists(aiZoneId).then(res => {
if (res && res.data && res.data.rows) {
setCate(res.data.rows)
}
})
break;
default:
aiZoneId && getSourceZoneList(aiZoneId, {
domainCategory: aiResouceTypeKey[type]
}).then(res => {
if (res && res.data && res.data.rows) {
setCate(res.data.rows)
}
})
break;
}
}, [type, aiZoneId])
useEffect(() => {
if (sourceId) {
getAISourceById(sourceId).then(res => {
if (res && res.data && res.data.code === 200) {
const { fileIds, extendData, name, summary, domainId, fileList, auditStatus, remark: remarkText } = res.data.data || {}
// auditStatus=2
if (auditStatus === '2') {
setRemark(remarkText || "请修改后重新提交");
}
const fileInfo = {
fileId: fileList[0]?.fileId,
fileOriginName: fileList[0]?.fileOriginName,
fileSize: fileList[0]?.fileSizeInfo,
url: fileList[0]?.downloadUrl
}
setUploadedFile(fileInfo)
const fileValue = {
name,
summary,
domainId
}
type === "skills" ? fileValue.file = {
file: {
response: {
fileId: fileIds,
skillMdContent: extendData.SKILL.content,
fileTreeJson: JSON.stringify(extendData.SKILL.fileTree)
}
}
} : fileValue.fileId = fileIds
setFieldsValue(fileValue)
}
})
}
}, [sourceId])
//
const beforeUpload = (file, size = 10) => {
const limitSize = file.size / 1024 / 1024 < size;
if (!limitSize) {
message.error(`文件大小必须小于${size}MB!`);
}
return limitSize;
};
function submit() {
const compelete = (response) => {
if (response && response.data && response.data.code === 200) {
message.success("操作成功");
history.push(`/ai/${type}/my`);
}
}
validateFields((err, values) => {
if (!err) {
switch (type) {
case "codes":
axios.post(`/projects.json`, {
...values,
...info,
}).then(res => {
if (res && res.data && res.data.id) {
//
addProjectInZone([{
gitlinkProjectId: res.data.id,
projectTypeId: values.projectTypeId,
zoneId: aiZoneId
}]).then(response => { compelete(response) })
}
})
break;
case "skills":
const { fileId, skillMdContent } = responseByupload
const submitFn = sourceId ? editSkill : addSkill;
const param = {
...values,
fileId,
fileTreeJson,
skillContent: skillMdContent,
zoneId: aiZoneId
}
!!sourceId && (param.id = sourceId)
submitFn(param, roleByZone).then(response => { compelete(response) })
break;
default:
// models/datasets 使 fileId
const { fileId: formFileId } = getFieldsValue() || {};
const submitParam = {
...values,
fileId: formFileId,
zoneId: aiZoneId
}
if (sourceId) {
submitParam.id = sourceId;
}
const submitFn1 = type === "models" ? sourceId ? editModel : addModel : sourceId ? editDataset : addDataset;
submitFn1(submitParam).then(response => { compelete(response) })
break;
}
}
})
}
const codeCheckBox = (checkKey, checkName, value, selectKey, list) => {
return <Fragment>
<Form.Item
className="privatePart"
>
{getFieldDecorator(checkKey)(
<Checkbox>{checkName}</Checkbox>
)}
</Form.Item>
{value && <Form.Item>
{getFieldDecorator(selectKey, {
rules: [{
required: value, message: `请选择${checkName}`
}],
})(
<DorpChooseMenus idname={"newPanel"} name={`请选择${checkName}${checkName === "gitignore" ? ",用来定义哪些文件不需要添加到版本管理中" : ""}`} list={list} getValue={(value) => {
const value2 = {}
value2[selectKey] = value.name;
setFieldsValue(value2);
const value1 = { ...info }
value1[selectKey + "_id"] = value.id;
setInfo(value1)
}} />
)}
</Form.Item>
}
</Fragment>
}
return (
<div className="ai_create_page">
{/* 顶部导航 */}
<div className="ai_create_header center pt40">
<div className="ai_banner_title mb5">
发布
<span className="carousel_text mr20">新资源</span>
全新智算共享
</div>
<div className="font-16 mb40">
将您的模型数据集skill或代码库分享给社区
</div>
<div className="ai_create_banner_tabs df">
{aiResouceType.map((item) => (
<Link
to={`/ai/${item.key}/create`}
key={item.key}
className={`ai_create_banner_tab pointer ${type === item.key ? 'active' : ''} ${!!sourceId && "disabled"}`}
onClick={(e) => {
if (!!sourceId) {
e.preventDefault();
e.stopPropagation();
return;
}
form.resetFields();
if (uploadRef.current) {
uploadRef.current.cancelUpload();
}
}}
>
<i className={`${type === item.key ? 'iconfont icon-' : 'iconfont-color icon-color'}${item.icon} font-20`}></i>
<div className="font-16 font-bd mb4">{item.name}</div>
<div className="font-13 color3f4">{item.intro}</div>
</Link>
))}
</div>
</div>
{/* 主体内容 */}
<div className="ai_create_content ai_1600 df-start">
<div className="ai_create_content_left">
<div className="font-bd font-15 mb30">如何开始</div>
<div className="steps-container">
{aiCreateSteps.map((step, index) => (
<div
key={index}
className={`step-item df-start pb40 ${currentStep === index ? 'active' : ''}`}
>
<div className="step-number mr15">{index + 1}</div>
<div className="step-content flex1">
<div className="font-bd font-15">{step.title}</div>
<div className="font-13 color768">{step.description}</div>
</div>
{index < aiCreateSteps.length - 1 && (
<div className="step-line"></div>
)}
</div>
))}
</div>
</div>
<div className="ai_create_form_wrapper flex1" id="newPanel">
<div className="ai_create_form_header mb30">
<div className="font-20 font-bd mb5">基本信息</div>
<div className="font-15 color768">{typeDetail.createIntro}</div>
{remark && (
<div className="audit-rejected-wrapper mt20">
<Alert
message="审核驳回"
description={remark}
type="error"
showIcon
closable
/>
</div>
)}
</div>
<Form
form={form}
layout="vertical"
className="ai_form"
colon={false}
>
{type === "codes" ? <Fragment>
<AlignCenter>
<Form.Item
label="拥有者"
style={{ width: "260px" }}
>
{getFieldDecorator('user_id', {
rules: [{
required: true, message: '请选择拥有者'
}],
})(
<Select
showSearch
style={{ width: "260px", height: "33px" }}
placeholder="请选择拥有者"
className="plateAutoComplete"
optionFilterProp="children"
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{ownerList && ownerList.length > 0 &&
ownerList.map((i, k) => {
return (
<Option value={i.id}>{i.name}</Option>
)
})
}
</Select>
)}
</Form.Item>
<span className="ml10 mr10 mt10 font-18">/</span>
<Form.Item
label="项目名称"
className="flex1"
>
{getFieldDecorator('name', {
rules: [{
required: true, message: '请填写项目名称'
}],
})(
<Input placeholder="例如:团队协作方法与研究" maxLength={50} />// onBlur={this.changeName}
)}
</Form.Item>
</AlignCenter>
<Form.Item
label={<span>项目标识 <span className="color-grey-9">(项目url标识部分)</span></span>}
>
{getFieldDecorator('repository_name', {
rules: [{
required: true, message: '请填写项目标识'
}],
})(
<Input placeholder="项目标识请使用与项目相关的英文关键字" maxLength={100} />
)}
</Form.Item>
<Form.Item
label="项目分类"
>
{getFieldDecorator('projectTypeId', {
rules: [{
required: true, message: '请选择项目分类'
}],
})(
<Select
placeholder="请选择项目分类"
optionFilterProp="children"
>
{cate && cate.length > 0 &&
cate.map((i, k) => {
return (
<Option value={i.id}>{i.name}</Option>
)
})
}
</Select>
)}
</Form.Item>
<Form.Item
label="简介"
>
{getFieldDecorator('description', {
rules: [],
})(
<Input.TextArea maxLength={200} placeholder="请输入简介内容不超过200字" autoSize={{ minRows: 2, maxRows: 6 }} />
)}
</Form.Item>
{codeCheckBox("ignoreFlag", ".gitignore", ignoreFlag, "ignore", gitignoreList)}
{codeCheckBox("licenseFlag", "开源许可证", licenseFlag, "license", licensesList)}
{/* <Form.Item
className="privatePart"
>
{getFieldDecorator('private')(
<Checkbox value="limit">将项目设为私有<span className="font-13 color-grey-9">(只有项目所有人或拥有权限的项目成员才能看到)</span></Checkbox>
)}
</Form.Item > */}
{codeCheckBox("categoreFlag", "项目类别", categoreFlag, "project_category", categoryList)}
{codeCheckBox("languageFlag", "项目语言", languageFlag, "project_language", languageList)}
</Fragment> : <Fragment>
{type === "skills" && <Form.Item label={`${typeDetail.name}文件`}>
{getFieldDecorator('file', {
rules: [{ required: true, message: "请上传文件" }],
})(
responseByupload ? <Spin spinning={loading}>
<div className="edu-txt-right mb10"><a onClick={() => {
form.setFieldsValue({
file: undefined
})
}}>重新上传清空</a></div>
<Tree.DirectoryTree
className="ai_tree_file"
treeData={skillTreeData}
selectable={false}
/>
</Spin> : <Upload
accept={".zip"}
beforeUpload={(file) => beforeUpload(file, 10)}
showUploadList={false}
customRequest={(options) => {
const { file, onSuccess } = options;
const form = new FormData();
form.append('file', file);
setLoading(true);
parseFileBySkill(form, roleByZone).then((response) => {
if (response && response.data && response.data.code === 200) {
onSuccess(response.data.data)
const { summary, name } = response.data.data;
setFieldsValue({
summary,
name
})
}
}).finally(() => {
setLoading(false);
})
}}
>
<div className="ai_create_upload center cursor">
<span className="ai_create_upload_icon">
<i className="iconfont icon-shangchuanicon color091"></i>
</span>
<div className="color091 mt15 mb15">请上传您的{typeDetail.name}文件.zip</div>
<div className="coloraeb">请确保压缩包中包含SKILL.md大小限制 10MB</div>
</div>
</Upload>
)}
</Form.Item>}
<Form.Item label="名称">
{getFieldDecorator('name', {
rules: [{
required: true, message: '请填写名称'
}],
})(
<Input placeholder="请输入名称" maxLength={50} />
)}
</Form.Item>
<Form.Item label="简介">
{getFieldDecorator('summary', {
rules: [{
required: false, message: '请填写简介内容'
}],
})(
<Input.TextArea maxLength={500} placeholder="请输入简介内容不超过500字" autoSize={{ minRows: 2 }} />
)}
</Form.Item>
{/* 资源类型 */}
<Form.Item label="类别">
{getFieldDecorator('domainId', {
rules: [{
required: true, message: '请选择类别'
}],
})(
<Select placeholder="请选择类别" className="font-15">
{cate && cate.length > 0 &&
cate.map((i, k) => {
return (
<Option value={i.id}>{i.name}</Option>
)
})
}
</Select>
)}
</Form.Item>
{/* models/datasets 类型使用分片上传 */}
{type !== "skills" && <Form.Item label={`${typeDetail.name}文件`} required>
{getFieldDecorator('fileId', {
rules: [{
required: true, message: `请上传${typeDetail.name}文件`
}],
})(
<div>
<ChunkedUpload
hierarchy={`${type}`}
accept=".zip"
defaultFile={uploadedFile}
ref={uploadRef}
fileType={"ai-resource"}
disabled={loading}
maxSize={1024 * 1024 * 1024 * 1024} // 1TB
onUploadComplete={(fileInfo) => {
// fileId
setFieldsValue({
fileId: fileInfo.fileId
});
}}
onCancel={() => {
setUploadedFile(null);
setFieldsValue({
fileId: undefined
});
}}
>
<span className="ai_create_upload_icon">
<i className="iconfont icon-shangchuanicon color091"></i>
</span>
<div className="color091 mt15 mb15">请上传{typeDetail.name}压缩包支持 .zip 格式</div>
<div className="coloraeb mb10">请确保压缩包内包含{typeDetail.name}文件及 README.md系统将自动解析说明文档</div>
<div className="coloraeb">单文件最大支持 1TB系统支持断点续传</div>
</ChunkedUpload>
</div>
)}
</Form.Item>}
</Fragment>}
{/* 提交按钮 */}
<Form.Item className="mt36">
<a className="btn-outline mr25" href={sourceId ? `/ai/${type}/my` : `/ai/${type}`}> </a>
<a className="btn-black" loading={loading} onClick={submit}> </a>
</Form.Item>
</Form>
</div>
</div>
</div>
);
}
export default Form.create()(CreateResource);

View File

@ -1,153 +0,0 @@
.ai_create_page {
background: #f8f9fc url('../image/bk2.png');
background-repeat: no-repeat;
background-size: 100% auto;
.ai_create_banner_tabs {
justify-content: center;
gap: 36px;
.ai_create_banner_tab {
background: linear-gradient(87.55deg, #eef3fd 3.07%, #ffffff 98.94%);
border: 1px solid rgba(22, 76, 228, 0.1);
border-radius: 12px;
padding: 34px 92px;
&:nth-child(4n+2) {
background: linear-gradient(87.55deg, #fdf5fd 3.07%, #ffffff 98.94%);
}
&:nth-child(4n+3) {
background: linear-gradient(87.55deg, #eef3fd 3.07%, #ffffff 98.94%);
}
&:nth-child(4n) {
background: linear-gradient(87.55deg, #ececff 3.07%, #f8f9fd 98.94%);
}
&.active {
background-image: url('../image/card_bk2.png');
background-size: 100% 100%;
border-color: white;
&,
& .color3f4 {
color: white;
}
}
&:hover:not(.active):not(.disabled) {
transition: all 0.3s ease;
transform: translateY(-10px);
border-color: #164ce4;
}
&:not(.active),
&:not(.active):hover,
&:not(.active):focus {
color: inherit;
}
}
}
// 主体内容
.ai_create_content {
padding: 68px 0;
}
// 步骤引导组件样式
.ai_create_content_left {
flex: 0 0 380px;
margin-right: 30px;
background: #ffffff;
border: 1px solid rgba(22, 76, 228, 0.1);
border-radius: 12px;
padding: 30px 35px;
.steps-container {
.step-number {
width: 36px;
height: 36px;
border-radius: 50%;
background: #eef4fe;
border: 2px solid rgba(22, 76, 228, 0.1);
line-height: 36px;
text-align: center;
color: #aebbd0;
font-size: 16px;
font-family: "DouyinSansBold";
position: relative;
}
}
.step-item.active {
.step-number {
background-image: url('../image/icon4.png');
background-size: 100% 100%;
color: white;
border: none;
}
}
.step-item:not(:last-of-type) {
.step-number::before {
content: '';
position: absolute;
top: 50px;
left: 15px;
width: 0px;
height: 58px;
border-left: 1px dashed rgba(174, 187, 208, 0.7);
}
}
}
.ai_create_form_wrapper {
background: #ffffff;
border: 1px solid rgba(22, 76, 228, 0.1);
border-radius: 12px;
padding: 55px 60px;
}
.btn-black,
.btn-outline {
font-size: 15px;
padding: 11px 54px;
border-radius: 10px;
}
.ai_form {
width: 68.5%;
.ant-upload.ant-upload-select {
width: 100%;
}
// 审核驳回提示样式
.audit-rejected-wrapper {
.ant-alert-error {
border-radius: 8px;
}
}
}
.ai_create_upload {
padding: 30px 0;
border: 1px dashed rgba(22, 76, 228, 0.17);
border-radius: 6px;
}
.ai_create_upload_icon {
display: inline-block;
width: 45px;
height: 45px;
background: #f3f4f8;
border-radius: 50%;
line-height: 45px;
}
.ai_tree_file{
border: 1px solid #D9D9D9;
border-radius: 6px;
padding: 5px 10px;
}
}

View File

@ -1,106 +0,0 @@
import React, { useEffect, useState } from "react";
import { withRouter } from "react-router";
import { httpUrl } from '../../ai/fetch';
import cookie from 'react-cookies';
import { aiAgentLogin } from "../../forge/Information/api";
import { agentLogin } from "../api";
function Agent(props) {
const { match, current_user, history, showLoginDialog } = props;
const { id } = match.params;
const { login } = current_user || {};
const [iframeUrl, setIframeUrl] = useState('');
useEffect(() => {
document.title = "智能体-AI资源";
checkLoginAndGetIframeUrl();
}, []);
// iframe URL
const checkLoginAndGetIframeUrl = async () => {
try {
//
if (!login) {
history.push(`/login?go_page=/ai/agents/${id}`);
return;
}
// Cookieagent
const agentCookie = cookie.load('agent');
let currentTokenData = null;
if (!agentCookie) {
// Cookieagent
const res = await agentLogin({
grant_type: 'password',
client_id: '4b25ca036879216a020a',
username: 'hnxjy',
password: '123456',
scope: 'profile'
});
if (res && res.data && res.data) {
const { access_token, expires_in } = res.data;
// + expires_in
const expiryDate = new Date(Date.now() + (expires_in) * 1000);
// tokenagent Cookie
currentTokenData = access_token;
cookie.save('agent', currentTokenData, { expires: expiryDate, path: '/' });
}
} else {
// CookieCookietoken
currentTokenData = agentCookie;
}
// iframe URLtoken
const url = getIframeUrl(id, currentTokenData);
setIframeUrl(url);
} catch (err) {
console.error('检查登录状态失败:', err);
}
};
// iframe URLtokenURL
const getIframeUrl = (agentId, accessToken) => {
if (!httpUrl || !agentId) return '';
try {
const urlObj = new URL(httpUrl);
const baseOrigin = `${urlObj.protocol}//${urlObj.host}`;
let url = `${baseOrigin}/chat/${agentId}`;
// tokenURL
if (accessToken) {
const separator = url.includes('?') ? '&' : '?';
url += `${separator}token=${encodeURIComponent(accessToken)}`;
}
return url;
} catch (e) {
return '';
}
};
return (
<div className="agent-container" style={{ width: '100%', height: '100vh', overflow: 'hidden' }}>
{iframeUrl ? (
<iframe
src={iframeUrl}
style={{
width: '100%',
height: '100%',
border: 'none',
}}
allow="clipboard-read; clipboard-write"
/>
) : (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%' }}>
加载中...
</div>
)}
</div>
);
}
export default withRouter(Agent);

View File

@ -1,299 +0,0 @@
import React, { useState, useEffect, Fragment } from "react";
import { Tabs, Tag, Spin, message, Divider, Tree, Tooltip, Button, Badge } from "antd";
import RenderHtml from "../../components/render-html";
import "./index.scss";
import { getlikedSource, getAISourceById, likeAISource, editModel, editDataset } from "../../forge/Information/api";
import { aiResouceStatus, aiResouceType, aiResouceTypeKey, convertToTreeData } from "../static";
import skillIcon from '../image/icon7.png';
import CommentsList from "../../components/commentsByZone";
import TpmMdEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
const { TabPane } = Tabs;
function Detail(props) {
//
const { history, match: { params }, current_user: { login }, current_user, showLoginDialog, showNotification } = props;
const { id, type } = params || {};
//
const [reload, setReload] = useState(undefined);
const [detail, setDetail] = useState({});
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState("introduction");
const [liked, setLiked] = useState(false);
const [isEditor, setIsEditor] = useState(false);
const [editContent, setEditContent] = useState("");
const [saving, setSaving] = useState(false);
const { extendData={}, createBy, auditStatus } = detail;
const status = aiResouceStatus[auditStatus] || {};
const typeDetail = type && aiResouceType.find(i => i.key === type);
const name = typeDetail && typeDetail.name
const dataByType = extendData[aiResouceTypeKey[type]] || {}
const content = dataByType.content || dataByType.readmeContent
const introContent = String(content || `<div className="ai_detail_nodata_text">当前${name}未提供详细介绍</div>`).replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n/, '');
const treeData = dataByType && dataByType.fileTree && convertToTreeData(dataByType.fileTree.children)
//
const isPublisher = login && createBy && login === createBy;
// ID
useEffect(() => {
document.title = "AI资源";
setLoading(true);
getDetail()
getIsLikedSource()
}, [id, reload]); // id id
const getDetail = () => {
id && getAISourceById(id).then(response => {
if (response && response.data && response.data.code === 200) {
const { name } = response.data.data
document.title = `${name}-AI资源`;
setDetail(response.data.data);
setLoading(false);
}
})
}
const getIsLikedSource = () => {
id && getlikedSource(id).then(response => {
if (response && response.data && response.data.code === 200) {
const { isLike } = response.data.rows[0] || {};
setLiked(isLike);
}
})
}
//
const handleToggleEdit = () => {
if (isEditor) {
//
setEditContent(content || "");
setIsEditor(false);
} else {
//
setEditContent(content || "");
setIsEditor(true);
}
}
//
const handleSave = () => {
setSaving(true);
const editFun = type === "models" ? editModel : editDataset
editFun({
id: +id,
readmeContent: editContent
}).then(response => {
if (response && response.data && response.data.code === 200) {
message.success("保存成功");
setIsEditor(false);
setReload(Date.now());
} else {
message.error(response?.data?.msg || "保存失败");
}
}).catch(() => {
message.error("保存失败,请重试");
}).finally(() => {
setSaving(false);
});
}
//
const formatDownloads = (num) => {
if (num >= 10000) {
return (num / 10000).toFixed(1) + "万";
}
return num;
};
if (loading) {
return (
<div className="ai_detail_loading">
<Spin size="large" />
</div>
);
}
if (!detail) {
return (
<div className="ai_detail_nodata">
<div className="font-16 color768">暂无数据</div>
</div>
);
}
return (
<div className="ai_detail_page">
{/* 顶部信息卡片 */}
<div className="ai_detail_header ai_1600 mb40">
<div className="ai_detail_header_inner">
<div className="df-start">
<img src={skillIcon} width={40} className="mr15" />
<div className="mt-10 flex1">
<div className="df-start">
<div className="ai_detail_header_info mrat">
<div className="font-20 font-bd">{detail.name}</div>
<div className="color3f4">{detail.summary || "暂无简介"}</div>
<div className="ai_detail_header_tags mt10 df-center">
{detail.tags && detail.tags.map((tag, index) => (
<Tag key={index} className="ai_detail_header_tag">
{tag}
</Tag>
))}
{detail.license && (
<Tag className="ai_detail_header_tag">
开源协议{detail.license}
</Tag>
)}
{detail.domainName && (
<Tag className="ai_detail_header_tag">
{detail.domainName}
</Tag>
)}
{dataByType && dataByType.fileSize && (
<Tag className="ai_detail_header_tag">
{dataByType.fileSize}
</Tag>
)}
</div>
</div>
<div className="shrink0 mt20 ml20">
{detail.fileList && detail.fileList[0] && <a className="btn-black font-15 mr20" href={detail.fileList[0].downloadUrl} download onClick={(e) => {
if (!login) {
showLoginDialog();
e.stopPropagation();
e.preventDefault();
}
}}>下载</a>}
<a className="btn-outline font-15" onClick={() => history.goBack()}>返回</a>
</div>
</div>
<div>
<Divider dashed className="mt20 mb20" />
<div className="df-center color828">
{auditStatus !== "1" && <Fragment>
<Badge color={status.color} text={status.label} className='status-dot' />
<Divider type="vertical" style={{ height: '7px', margin: '0 20px' }} />
</Fragment>}
<i className="iconfont icon-yonghuming font-14 mr8"></i>
{detail.createUser && detail.createUser.userName || "未知作者"}
<Divider type="vertical" style={{ height: '7px', margin: '0 20px' }} />
<i className="iconfont icon-a-shijianzhongbiao font-14 mr8"></i>
{moment(detail.updateTime).format('YYYY.MM.DD')}
<Divider type="vertical" style={{ height: '7px', margin: '0 20px' }} />
<i className="iconfont icon-xiazai3 font-14 mr8"></i>
{formatDownloads(detail.downloadCount)}
<Divider type="vertical" style={{ height: '7px', margin: '0 20px' }} />
<Tooltip title={liked ? "取消点赞" : "点赞"}>
<a className="likeBtn" onClick={() => {
if (!login) {
showLoginDialog()
}
likeAISource(id).then(res => {
if (res && res.data && res.data.code === 200) {
message.success("操作成功")
getDetail();
getIsLikedSource();
}
})
}}><i className="iconfont icon-dianzan5 font-14 mr8"></i></a>
</Tooltip>
{detail.likeCount}
</div>
</div>
</div>
</div>
</div>
</div>
{/* Tab 导航 */}
<div className="ai_detail_tabs_wrapper ai_1600">
<Tabs
className="ai_detail_tabs"
activeKey={activeTab}
onChange={(tab) => setActiveTab(tab)}
centered
>
<TabPane
tab={`${name}介绍`}
key="introduction"
/>
<TabPane
tab={`${name}文件`}
key="files"
/>
<TabPane
tab={`交流反馈`}
key="comments"
/>
</Tabs>
</div>
<div className="ai_detail_content ai_1600">
{activeTab === "introduction" && (
<div className="ai_detail_tab_panel">
{/* 编辑按钮区域 - 仅发布者可见 */}
{isPublisher && ["models", "datasets"].includes(type) && !isEditor && <div className="ai_detail_edit_toolbar mb15">
{auditStatus === "1" ? (
<Tooltip getPopupContainer={trigger => trigger.parentNode} title={<span className="color-grey-98">当前资源是上架状态请前往<br/><a href={`/ai/${type}/my`} className="color-white">我的资源池</a>页面先进行下架</span>}>
<a className="btn-outline pt5 pb5" style={{ cursor: 'not-allowed', color: '#999', borderColor: '#999' }}>编辑</a>
</Tooltip>
) : (
<a className="btn-outline pt5 pb5" onClick={handleToggleEdit}>编辑</a>
)}
</div>}
{isEditor ? (
<Fragment>
<div className="ai_detail_readme_editor">
<TpmMdEditor
initValue={editContent}
onChange={(value) => setEditContent(value)}
height={600}
mdID={`ai_detail_readme_editor_${id}`}
/>
</div>
<div className="edu-txt-right mt40">
<Button className="mr10" onClick={handleToggleEdit}>取消</Button>
<Button type="primary" loading={saving} onClick={handleSave}>保存</Button>
</div>
</Fragment>
) : (
<div className="ai_detail_readme">
<RenderHtml value={introContent} />
</div>
)}
</div>
)}
{activeTab === "files" && (
<div className="ai_detail_tab_panel">
<Tree.DirectoryTree
className="ai_detail_tree_node"
defaultExpandAll={false}
treeData={treeData}
selectable={false}
blockNode={true}
/>
</div>
)}
{activeTab === "comments" && (
<div className="ai_detail_tab_panel ai_detail_comments">
<CommentsList
type={"resource"}
id={id}
{...props}
/>
</div>
)}
</div>
</div>
);
}
export default Detail;

View File

@ -1,385 +0,0 @@
.ai_detail_page {
background: #f8f9fc url('../image/bk2.png');
background-repeat: no-repeat;
background-size: 100% auto;
padding-top: 45px;
padding-bottom: 50px;
min-height: 89vh;
}
// 加载状态
.ai_detail_loading,
.ai_detail_nodata {
display: flex;
align-items: center;
justify-content: center;
min-height: 500px;
}
.ai_detail_nodata_text {
color: #76849b;
font-size: 14px;
}
// 顶部信息卡片
.ai_detail_header {
background-image: url('../image/bk3.png');
background-size: 100% 100%;
padding: 14px 13px;
border-radius: 6px;
.ai_detail_header_inner {
background: linear-gradient(90.39deg, #ffffff 1.15%, #ebf1ff 18.37%, #f4f3fd 98.25%);
border-radius: 6px;
padding: 30px;
.btn-black, .btn-outline {
padding: 7px 32px;
border-radius:10px;
}
.btn-black{
border: 1px solid #091221;
}
.ai_detail_header_tag{
color: #091221;
border-color:#d3e1fd;
background:#f5f9fd;
padding: 6px 16px 5px 16px;
font-size: 13px;
}
.likeBtn{
color: inherit;
&:hover{
color:#164ce4;
}
}
}
}
.ai_detail_tabs {
.ant-tabs-bar {
border-bottom: none;
}
.ant-tabs-ink-bar {
background: #164ce4;
border-radius: 10px;
height: 3px;
width: 20px !important;
left: 20px;
}
.ant-tabs-tab {
padding: 12px 0px;
font-size: 15px;
color: #091221;
margin-right: 52px;
&:hover {
color: #164ce4;
}
&.ant-tabs-tab-active {
color: #164ce4;
font-weight: 600;
}
}
}
// Tab 内容
.ai_detail_tab_panel {
background: #ffffff;
border: 1px solid rgba(22, 76, 228, 0.1);
border-radius: 12px;
padding: 30px;
min-height: 365px;
// 模型介绍
// .ai_detail_readme {
// .markdown-body {
// h1,
// h2,
// h3,
// h4,
// h5,
// h6 {
// color: #091221;
// margin-top: 24px;
// margin-bottom: 16px;
// font-weight: 600;
// line-height: 1.25;
// }
// h1 {
// font-size: 28px;
// }
// h2 {
// font-size: 24px;
// }
// h3 {
// font-size: 20px;
// }
// h4 {
// font-size: 18px;
// }
// p {
// font-size: 15px;
// line-height: 1.8;
// color: #333;
// margin: 10px 0;
// }
// a {
// color: #0e4aef;
// text-decoration: none;
// &:hover {
// text-decoration: underline;
// }
// }
// ul,
// ol {
// margin: 15px 0;
// padding-left: 30px;
// li {
// font-size: 15px;
// line-height: 1.8;
// color: #333;
// }
// }
// code {
// background: #f5f7fa;
// padding: 2px 6px;
// border-radius: 4px;
// font-family: "Courier New", monospace;
// font-size: 14px;
// color: #d63384;
// }
// pre {
// background: #282c34;
// padding: 16px;
// border-radius: 8px;
// overflow-x: auto;
// code {
// background: none;
// padding: 0;
// color: #abb2bf;
// }
// }
// blockquote {
// border-left: 4px solid #0e4aef;
// padding: 12px 16px;
// margin: 16px 0;
// background: #f5f7fa;
// border-radius: 4px;
// p {
// margin: 0;
// color: #666;
// }
// }
// table {
// width: 100%;
// margin: 16px 0;
// border-collapse: collapse;
// th,
// td {
// padding: 12px 16px;
// border: 1px solid #e1e4e8;
// text-align: left;
// }
// th {
// background: #f5f7fa;
// font-weight: 600;
// color: #091221;
// }
// td {
// color: #333;
// }
// }
// img {
// max-width: 100%;
// height: auto;
// border-radius: 8px;
// margin: 16px 0;
// }
// }
// }
// 模型文件
.ai_detail_files_header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 15px;
.ai_detail_branch_select {
.ant-select-selector {
border-radius: 8px;
padding: 8px 12px;
}
}
.ai_detail_commit_info {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 10px;
.ai_detail_commit_message {
color: #091221;
font-size: 14px;
max-width: 400px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ai_detail_commit_author {
color: #667eea;
font-size: 14px;
}
.ai_detail_commit_sha {
font-family: "Courier New", monospace;
}
.ai_detail_commit_time {
font-size: 14px;
}
}
.ai_detail_commit_count {
font-size: 14px;
}
}
.ai_detail_files_list {
.ai_detail_file_item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px 20px;
margin-bottom: 10px;
background: #f8f9fd;
border-radius: 8px;
transition: all 0.3s;
&:hover {
background: #f1f3ff;
border: 1px solid #667eea;
}
.ai_detail_file_info {
display: flex;
align-items: center;
.ai_detail_file_name {
color: #091221;
font-weight: 500;
}
.ai_detail_file_size {
color: #76849b;
}
}
.ai_detail_file_meta {
display: flex;
align-items: center;
flex: 1;
margin-right: 20px;
.ai_detail_file_message {
color: #76849b;
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.ai_detail_file_time {
color: #76849b;
}
}
.ai_detail_file_download {
color: #0e4aef;
padding: 8px 16px;
border-radius: 6px;
transition: all 0.3s;
&:hover {
background: #0e4aef;
color: #fff;
}
}
}
}
// 交流反馈
&.ai_detail_comments {
--primary-color: #466aff;
--light-color: rgba(70,106,255,0.85);
--more-light-color: rgba(70, 106, 255, 0.05);
--linear-back-color: linear-gradient(89.9deg,rgba(70, 106, 255, 0) 0%,rgba(70, 106, 255, 0.09) 100%);
--tag-back: rgba(70, 106, 255, 0.12);
--light-color-2: rgba(70,106,255,0.23);
--light-color-3: rgba(70,106,255,0.55);
}
// 文件树节点样式
.ai_detail_tree_node {
.ant-tree-title{
width: calc(100% - 42px);
display: inline-flex;
justify-content: space-between;
font-size: 15px;
color:#27264d;
}
li:not(.ant-tree-treenode-switcher-close):not(.ant-tree-treenode-switcher-open) .ant-tree-node-content-wrapper{
cursor: default;
}
}
// 编辑工具栏
.ai_detail_edit_toolbar {
padding: 0 4px;
text-align: right;
}
// Markdown 编辑器容器
.ai_detail_readme_editor {
// min-height: 500px;
// 覆盖 TpmMdEditor 的默认样式使其与页面风格一致
.markdown-editor {
border-radius: 8px;
.editor-textarea {
font-family: "Courier New", monospace;
font-size: 15px;
line-height: 1.8;
}
}
}
}

View File

@ -1,60 +0,0 @@
import axios from 'axios';
import { message , notification } from 'antd';
import cookie from 'react-cookies';
function beforeFetch(actionUrl){
if (window.location.href.indexOf('localhost') < 0) {
axios.defaults.withCredentials = true;
}
const config = {
// headers:{Authorization:"Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjo0NCwidXNlcl9rZXkiOiI3NWY3MmJmYmE2NGU4N2U3NWVjNWMwZDk2OThkMDFiN2EyODE0YjYwIiwidXNlcm5hbWUiOiJpbm5vdiJ9.IviAyZQjPFc4rT3ejAKfJYx3_QgS78RCKHx1XS-A4uI_mrc7_l6wK76HVIoPCEvP0Qimzyc6dpeVW4cE2fILZw"},
baseURL: actionUrl,
timeout: 1800000, // 请求超时时间
}
const service = axios.create(config);
service.interceptors.request.use(request => {
// let deptId = sessionStorage.getItem('deptId')
// request.headers.Authorization = cookie.load('autologin_trustie')
// if (deptId) {
// // 用于权限区分
// request.headers['Dept-Id'] = deptId
// }
return request
})
service.interceptors.response.use(
response => {
const res = response||{};
if(res && res.data && res.data.code === 404){
message.error("错误链接!");
window.location.href = '/nopage';
return Promise.reject('error');
}else if(res && res.data && res.data.code === 500){
message.error(res.data.msg);
return Promise.reject('error');
}else{
return response;
}
},
error => {
console.log(error);
// notification.open({
// message: "提示",
// description: error.message,
// });
// return Promise.reject(error);
}
)
return service;
}
let settings = localStorage.chromesetting&& localStorage.chromesetting !=="undefined"&& JSON.parse(localStorage.chromesetting);
// 根据当前IP判断使用哪个zone配置
const isSpecificIP = window.location.hostname.startsWith('27.');
// let actionUrl = settings && (isSpecificIP ? settings.common.zone : settings.common.zone_local) + '/zoneAdmin/api';
let actionUrl = settings && settings.common && settings.common.agent
const service = beforeFetch(actionUrl);
export const httpUrl = actionUrl;
export default service;

View File

@ -1,61 +0,0 @@
import axios from 'axios';
import { message , notification } from 'antd';
import cookie from 'react-cookies';
function beforeFetch(actionUrl){
if (window.location.href.indexOf('localhost') < 0) {
axios.defaults.withCredentials = true;
}
const config = {
// headers:{Authorization:"Bearer eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjo0NCwidXNlcl9rZXkiOiI3NWY3MmJmYmE2NGU4N2U3NWVjNWMwZDk2OThkMDFiN2EyODE0YjYwIiwidXNlcm5hbWUiOiJpbm5vdiJ9.IviAyZQjPFc4rT3ejAKfJYx3_QgS78RCKHx1XS-A4uI_mrc7_l6wK76HVIoPCEvP0Qimzyc6dpeVW4cE2fILZw"},
baseURL: actionUrl,
timeout: 1800000, // 请求超时时间
}
const service = axios.create(config);
service.interceptors.request.use(request => {
// let deptId = sessionStorage.getItem('deptId')
// request.headers.Authorization = cookie.load('autologin_trustie')
// if (deptId) {
// // 用于权限区分
// request.headers['Dept-Id'] = deptId
// }
return request
})
service.interceptors.response.use(
response => {
const res = response||{};
if(res && res.data && res.data.code === 404){
message.error("错误链接!");
window.location.href = '/nopage';
return Promise.reject('error');
}else if(res && res.data && res.data.code === 500){
message.error(res.data.msg);
return Promise.reject('error');
}else{
return response;
}
},
error => {
console.log(error);
// notification.open({
// message: "提示",
// description: error.message,
// });
// return Promise.reject(error);
}
)
return service;
}
let settings = localStorage.chromesetting&& localStorage.chromesetting !=="undefined"&& JSON.parse(localStorage.chromesetting);
// 根据当前IP判断使用哪个zone配置
const isSpecificIP = window.location.hostname.startsWith('27.');
// let actionUrl = settings && (isSpecificIP ? settings.common.zone : settings.common.zone_local) + '/zoneAdmin/api';
let actionUrl = settings && settings.common && settings.common.agent_login;
const service = beforeFetch(actionUrl);
export const httpUrl = actionUrl;
export default service;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 587 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

View File

@ -1,113 +0,0 @@
import React, { useEffect, useState } from "react";
import { ImageLayerOfCommentHOC } from "../modules/page/layers/ImageLayerOfCommentHOC";
import { Route, Switch, withRouter } from "react-router";
import { SnackbarHOC } from "educoder";
import { CNotificationHOC } from "../modules/courses/common/CNotificationHOC";
import { TPMIndexHOC } from "../modules/tpm/TPMIndexHOC";
import Loadable from 'react-loadable';
import Loading from "../Loading";
import './index.scss';
import { getCurrentRole, getMainInfos } from "../forge/Information/api";
const Detail = Loadable({
loader: () => import("./detail"),
loading: Loading,
})
const List = Loadable({
loader: () => import("./list"),
loading: Loading,
})
const Create = Loadable({
loader: () => import("./create"),
loading: Loading,
})
const MyResources = Loadable({
loader: () => import("./my-resources"),
loading: Loading,
})
const Agents = Loadable({
loader: () => import("./detail/agent"),
loading: Loading,
})
function AI(props) {
const { current_user: { login } } = props
const [aiZoneInfo, setAiZoneInfo] = useState(undefined);
const [roleByZone, setRoleByZone] = useState("Member");
const {id} = aiZoneInfo || {}
useEffect(() => {
getMainInfos("AI").then(res => {
if (res && res.data && res.data.data) {
setAiZoneInfo(res.data.data)
// deptIdheader
sessionStorage.setItem('deptId', res.data.data.deptId)
}
})
}, [])
useEffect(()=>{
if(id && login){
getCurrentRole(id);
// login && getCurrentRole(res.data.data.id).then(res1 => {
// if (res1 && res1.data && res1.data.data) {
// setRoleByZone(res1.data.data.role)
// }
// })
}
}, [id, login])
return <div className="ai_resouce_page">
<Switch {...props}>
<Route
path="/ai/agents/:id"
render={(p) => (
<Agents {...props} {...p} aiZoneInfo={aiZoneInfo} roleByZone={roleByZone}/>
)}
></Route>
<Route
path="/ai/:type/my"
render={(p) => (
<MyResources {...props} {...p} aiZoneInfo={aiZoneInfo} roleByZone={roleByZone}/>
)}
></Route>
<Route
path="/ai/:type/create"
render={(p) => (
<Create {...props} {...p} aiZoneInfo={aiZoneInfo} roleByZone={roleByZone}/>
)}
></Route>
<Route
path="/ai/:type/edit/:id"
render={(p) => (
<Create {...props} {...p} aiZoneInfo={aiZoneInfo} roleByZone={roleByZone}/>
)}
></Route>
<Route
path="/ai/:type/:id"
render={(p) => (
<Detail {...props} {...p} />
)}
></Route>
<Route
path="/ai/:type"
render={(p) => (
<List {...props} {...p} aiZoneInfo={aiZoneInfo} />
)}
></Route>
<Route
path="/ai"
render={(p) => (
<List {...props} {...p} aiZoneInfo={aiZoneInfo} />
)}
></Route>
</Switch>
</div>
}
export default withRouter(
ImageLayerOfCommentHOC({
imgSelector: ".imageLayerParent img, .imageLayerParent .imageTarget",
parentSelector: ".newMain",
})(CNotificationHOC()(SnackbarHOC()(TPMIndexHOC(AI))))
);

View File

@ -1,78 +0,0 @@
@import url('../animations.scss');
.ai_resouce_page {
font-family: "alibabaReg";
color: #091221;
.ai_1600 {
width: 1600px;
margin: 0 auto;
}
.color091 {
color: #091221;
}
.color768 {
color: #76849b;
}
.color3f4 {
color: #3f4f69;
}
.color828 {
color: #8285a5;
}
.coloraeb{
color:#aebbd0;
}
.mlat {
margin-left: auto;
}
.mrat {
margin-right: auto;
}
.shrink0{
flex-shrink: 0
}
.ai_banner_title {
font-size: 52px;
font-family: "DouyinSansBold";
.carousel_text {
background-image: linear-gradient(0.27deg, #0e4aef 1.35%, #a74eff 87.93%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
}
.btn-outline {
border: 1px solid #091221;
border-radius: 22px;
color: #091221;
font-size: 15px;
padding: 11px 20px;
}
.btn-black {
background: #091221;
border-radius: 9px;
color: #ffffff;
}
.arrowRight{
.iconfont {
display: inline-block;
animation: arrow-move 1.2s ease-in-out infinite;
position: relative;
top: 2px;
}
}
}

View File

@ -1,456 +0,0 @@
import { Fragment, useEffect, useState } from "react";
import './index.scss';
// Mock 12.md
const agentMockCate = [
{ id: "work", name: "工作" },
{ id: "study", name: "学习" },
{ id: "writing", name: "写作" },
{ id: "programming", name: "编程" },
{ id: "life", name: "生活" },
{ id: "health", name: "健康" }
];
const agentMockList = [
//
{
id: "meeting-minutes",
name: "会议纪要助手",
image: "",
desc: "专注于会议纪要的整理与提炼,帮助用户快速生成结构清晰、重点突出的会议纪要。",
cate: "work"
},
{
id: "project-management",
name: "项目管理助手",
image: "",
desc: "协助项目管理者进行任务拆解、进度跟踪、风险识别与资源协调,提升项目管理效率。",
cate: "work"
},
//
{
id: "knowledge-combination",
name: "知识梳理助手",
image: "",
desc: "帮助用户将零散的知识点系统化整理,构建知识框架,提升学习效率与记忆效果。",
cate: "study"
},
{
id: "training-planning",
name: "培训规划助手",
image: "",
desc: "根据岗位需求与员工能力现状,协助制定科学合理的培训计划与课程体系。",
cate: "study"
},
//
{
id: "official-writing",
name: "公文写作助手",
image: "",
desc: "专注于各类公文的撰写与润色,确保格式规范、用语准确、逻辑严密。",
cate: "writing"
},
{
id: "report-writing",
name: "报告撰写助手",
image: "",
desc: "协助用户撰写各类工作报告、调研报告、总结报告,提升报告的专业性与可读性。",
cate: "writing"
},
//
{
id: "code-review",
name: "代码审查助手",
image: "",
desc: "协助开发人员进行代码审查,发现潜在问题,提升代码质量与可维护性。",
cate: "programming"
},
{
id: "tech-documentation",
name: "技术文档助手",
image: "",
desc: "协助技术人员撰写 API 文档、系统设计文档、操作手册等技术文档,确保文档清晰准确。",
cate: "programming"
},
//
{
id: "schedule-planning",
name: "日程规划助手",
image: "",
desc: "协助用户合理安排日常工作与生活日程,提升时间利用效率,平衡工作与生活。",
cate: "life"
},
{
id: "meeting-arrangement",
name: "会议安排助手",
image: "",
desc: "协助用户高效安排会议时间、协调与会人员、准备会议材料,减少会议组织成本。",
cate: "life"
},
//
{
id: "schedule-management",
name: "作息管理助手",
image: "",
desc: "协助用户建立科学的作息规律,提供睡眠改善建议与作息调整方案,促进身心健康。",
cate: "health"
},
{
id: "exercise-planning",
name: "运动计划助手",
image: "",
desc: "根据用户的身体状况与运动目标,协助制定科学合理的运动计划,促进身体健康。",
cate: "health"
}
];
import { aiResouceType, aiResouceTypeKey, iconByCateIndex, iconByCateName } from "../static";
import icon1 from '../image/icon1.png';
import icon2 from '../image/icon2.png';
import icon3 from '../image/icon3.png';
import icon5 from '../image/icon5.png';
import icon6 from '../image/icon6.png';
import { Divider, Input, Pagination, Spin } from "antd";
import ActionItem from "../../factory/resource/components/ActionItem";
import { Link } from "react-router-dom";
import { getAISourceList, getProjectsLists, getProjectsTypeLists, getSourceZoneList } from "../../forge/Information/api";
import moment from "moment";
import Nodata from "../../forge/Nodata";
import { getAgentList, getAgentTypeList } from "../api";
import { httpUrl } from "../fetch";
function List(props) {
const { match: { params }, history, aiZoneInfo, current_user, showLoginDialog } = props || {};
const type = params.type || aiResouceType[0].key
const [activeCategory, setActiveCategory] = useState();
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [cate, setCate] = useState([]);
const [list, setList] = useState();
const [search, setSearch] = useState();
const [loading, setLoading] = useState(false);
const limit = 12;
const { id: aiZoneId } = aiZoneInfo || {}
const aiTypeInList = [
...aiResouceType,
{ key: "agents", name: "智能体", icon: "icon-zhinengti", intro: "驱动行业数智化", desc: "矩阵覆盖全场景需求,极速响应精准输出,低成本释放 AI 算力,赋能每一项任务。", createIntro: "上传您的智能体文件" }
]
const typeDetail = type && aiTypeInList.find(i => i.key === type);
useEffect(() => {
document.title = `AI资源广场`;
}, []);
// agents aiZoneId
useEffect(() => {
if (type === "agents") {
if(!httpUrl){
setCate(agentMockCate);
return;
}
getAgentTypeList().then(res => {
if (res && res.data && res.data.data) {
const data = res.data.data
setCate(data.map(item=>{
return {
...item,
name: item.typeName,
id: item.typeKey
}
}));
}
})
}
}, [type])
// aiZoneId aiZoneId
useEffect(() => {
if (type === "agents") return; // agents useEffect
if (!aiZoneId) return;
switch (type) {
case "codes":
getProjectsTypeLists(aiZoneId).then(res => {
if (res && res.data && res.data.rows) {
setCate(res.data.rows);
}
})
break;
default:
getSourceZoneList(aiZoneId, {
domainCategory: aiResouceTypeKey[type]
}).then(res => {
if (res && res.data && res.data.rows) {
setCate(res.data.rows);
}
})
break;
}
}, [type, aiZoneId])
useEffect(() => {
if (type === "agents") {
setLoading(true);
if(!httpUrl){
let filteredList = [...agentMockList];
if (activeCategory) {
filteredList = filteredList.filter(item => item.cate === activeCategory);
}
if (search) {
filteredList = filteredList.filter(item =>
item.name.toLowerCase().includes(search.toLowerCase()) ||
item.desc.toLowerCase().includes(search.toLowerCase())
);
}
const start = (page - 1) * limit;
setList(filteredList.slice(start, start + limit));
setTotal(filteredList.length);
setLoading(false);
return;
}
getAgentList({
page: page,
pageSize: limit,
search: search,
type: activeCategory
}).then(res => {
if (res && res.data && res.data.data) {
const data = res.data.data;
setList(data.pageData.map(row => {
const { botId, botCoverUrl, botName, botDesc } = row;
return {
id: botId,
name: botName,
image: botCoverUrl,
desc: botDesc,
}}
));
setTotal(data.totalCount);
}
}).finally(() => {
setLoading(false);
})
}
}, [type, activeCategory, page, search])
useEffect(() => {
if (type === "agents" || !aiZoneId) return
setLoading(true);
switch (type) {
case "codes":
getProjectsLists(aiZoneId, {
pageNum: page,
pageSize: limit,
projectTypeId: activeCategory,
name: search
}).then(res => {
if (res && res.data && res.data.rows) {
setList(res.data.rows.map(row => {
const { projectProperties, id } = row;
const imageUrl = projectProperties.authorImageUrl.split(':4000/') && projectProperties.authorImageUrl.split(':4000/')[1]
const authorImageUrl = imageUrl ? `${window.location.origin}/${imageUrl}` : projectProperties.authorImageUrl
return {
id: id,
name: projectProperties.fromOwner === "AI-RESOURCE" ? projectProperties.name.split("/").pop() : projectProperties.name,
image: authorImageUrl,
isNew: false,
url: projectProperties.fullName,
desc: projectProperties.description,
date: projectProperties.timeAgo,
forkedCount: projectProperties.forkedCount,
likes: projectProperties.praisesCount,
language: projectProperties.language ? projectProperties.language.split(",") : []
}
}))
setTotal(res.data.total)
}
}).finally(() => {
setLoading(false);
})
break;
default:
getAISourceList(aiZoneId, {
domainId: activeCategory,
pageNum: page,
pageSize: limit,
name: search,
resourceCategory: aiResouceTypeKey[type]
}).then(res => {
if (res && res.data && res.data.rows) {
setList(res.data.rows.map(row => {
const { name, id, summary, updateTime, downloadCount, likeCount, domainName } = row;
return {
id: id,
name: name,
isNew: false,
desc: summary,
date: moment(updateTime).format('YYYY.MM.DD'),
downloads: downloadCount,
likes: likeCount,
domainName
}
}))
setTotal(res.data.total)
}
}).finally(() => {
setLoading(false);
})
break;
}
}, [type, aiZoneId, activeCategory, page, search])
const aiCard = (model) => {
return <Fragment>
<i className="iconfont icon-a-shijianzhongbiao font-13 mr8"></i>
<span>{model.date}</span>
<Divider type="vertical" style={{ height: '7px', margin: '0 18px' }} />
{Object.prototype.hasOwnProperty.call(model, "downloads") && <Fragment>
<i className="iconfont icon-xiazai3 font-12 mr8"></i>
<span>{model.downloads}</span>
<Divider type="vertical" style={{ height: '7px', margin: '0 18px' }} />
</Fragment>}
{Object.prototype.hasOwnProperty.call(model, "forkedCount") && <Fragment>
<i className="iconfont icon-fork font-14 mr8"></i>
<span>{model.forkedCount}</span>
<Divider type="vertical" style={{ height: '7px', margin: '0 18px' }} />
</Fragment>}
<i className="iconfont icon-dianzan5 font-12 mr8"></i>
<span>{model.likes}</span>
</Fragment>
}
return (
<div className="ai_home_page">
{/* Banner 区域 */}
<div className="ai_home_banner center">
<div className="ai_home_banner_title mb10 ai_banner_title">
<span className="carousel_text mr40">AI资源广场</span>
<span>汇聚无限创意</span>
<div className="ai_home_banner_stars">
<img src={icon2} className="star_1" />
<img src={icon3} className="star_2" />
<img src={icon1} width={49} className="star_3" />
</div>
</div>
<div className="font-16 mb40">
面向开发者提供可信合规高效的 AI 资产分发中心涵盖百亿级模型权重库千万量级专业数据集托管代码仓及<br />智能skill中心实现从资源检索安全审计到业务集成的一体化研发生态
</div>
<div className="ai_home_banner_tabs">
{aiTypeInList.map((item) => (
<Link
to={`/ai/${item.key}`}
onClick={()=>{setActiveCategory(undefined); setList([])}}
key={item.key}
className={`ai_home_banner_tab font-15 pointer ${type === item.key ? 'active' : ''}`}
>
<i className={`iconfont ${item.icon} font-14 mr10`}></i>
<i className={`${type === item.key ? 'iconfont icon-' : 'iconfont-color icon-color'}${item.icon} font-14 mr10`}></i>
{item.name}
</Link>
))}
</div>
</div>
{/* 主体内容区域 */}
<div className="ai_home_content ai_1600 pb100">
{/* 左侧分类筛选 */}
<div className="ai_home_sidebar">
<div className="font-22 font-bd">全部{typeDetail.name}</div>
<div className="font-16 color768 mt10 mb30">
{typeDetail.desc}
</div>
<ul className="ai_home_sidebar_list">
<li
className={`ai_home_sidebar_item mb30 ${!activeCategory ? 'active' : ''}`}
onClick={() => setActiveCategory(undefined)}
>
{type !== "agents" && <i className={`${!activeCategory ? "iconfont icon-remenmoxing-2" : "iconfont-color icon-colorremenmoxing-1"} font-14 mr10`}></i>}
全部
</li>
{cate.map((category, index) => {
const icons = iconByCateName[category.name] ? iconByCateName[category.name] : (iconByCateIndex[type] && iconByCateIndex[type][index]) || ["iconfont-color icon-colorkexueyanjiuhejishufuwuye", "iconfont icon-a-kexueyanjiuhejishufuwuye-1"]
return <li
key={category.id}
className={`ai_home_sidebar_item mb30 ${activeCategory === category.id ? 'active' : ''}`}
onClick={() => setActiveCategory(category.id)}
>
{type !== "agents" && <i className={`${icons[Number(activeCategory === category.id)]} font-14 mr10`}></i>}
{category.name}
</li>
})}
</ul>
</div>
{/* 右侧内容区 */}
<div className="ai_home_main flex1">
{/* 搜索和操作栏 */}
<div className="ai_home_toolbar">
<Input.Search className="ai_home_search flex1" placeholder={`输入关键词搜索您想要的${typeDetail.name}(共${total}个)`} enterButton={<i className="iconfont icon-sousuo5 font-15"></i>} onSearch={(value) => { setPage(1); setSearch(value) }} />
{current_user && current_user.login && type !== "agents" && <div className="">
<a className="btn-outline font-bd mr10" href={`/ai/${type}/create`}> 新建 </a>
<a className="btn-outline font-bd ai_home_action_my" href={`/ai/${type}/my`}>我的{typeDetail.name}<i className="iconfont icon-arrright ml5"></i></a>
</div>}
</div>
{/* 模型列表 */}
<Spin spinning={loading}>
{list && !!list.length ? <div className={`ai_home_list ${type}_pattern`}>
{list.map((model) => (
<ActionItem key={`${type}-${model.id}`} className="ai_home_card" padding="1px" borderRadius="12px">
{type === "agents" ? <div>
<div className="df">
<img src={model.image ? model.image : require(`../image/${model.id}.png`).default} width={46} className="mr15" style={{ borderRadius: '8px' }} />
<span className="font-18 font-bd task-hide">{model.name}</span>
</div>
<div className="color3f4 mt15 task-hide-2" style={{minHeight: 56}}>{model.desc}</div>
<div className="btn-Box">
{httpUrl ? <a className="btn-black" href={`/ai/${type}/${model.id}`} onClick={(e) => { if (!current_user || !current_user.login) { e.preventDefault(); e.stopPropagation(); showLoginDialog(); } }}>立即使用</a> : <a className="unClickBtn">暂未开放</a>}
</div>
</div>:<div>
<div className="df-center">
{model.image && <img src={model.image} width={38} className="mr15" style={{ borderRadius: '8px' }} />}
{model.icon && type !== "skills" && <i className={`iconfont ${model.icon} font-30 mr15`}></i>}
<div className="flex1 task-hide mr10" style={{minHeight: '40px'}}>
<div className="font-16 font-bd">{model.name}{model.isNew && <i className="iconfont-color icon-colora-zuixin1 font-20 ml10"></i>}</div>
{model.desc && <div className="color3f4">{model.desc}</div>}
</div>
<a className="btn-black arrowRight" href={type === "codes" ? `/${model.url}` : `/ai/${type}/${model.id}`}>查看<i className="iconfont icon-arrright"></i></a>
</div>
{type === "skills" ? <div className="df-center skills_bottom color828">
<img src={icon6} width={18} />
<span className="mrat ml10 color091 font-13">{model.domainName}</span>
{aiCard(model)}
</div> : <Fragment>
{type === "codes" ? <div className="mt15"></div> : <Divider dashed />}
<div className="df-center color828">
{aiCard(model)}
{type === "codes" ? <div className="df-center mlat">
{model.language && !!model.language.length && <Fragment>
<img src={icon5} className="mr8 mt1" width={18} />
{model.language.map((topic, index) => {
return <Fragment>
<span className="color091">{topic}</span>
{index < model.language.length - 1 && <Divider type="vertical" style={{ height: '7px' }} className="mt5" />}
</Fragment>
})}
</Fragment>}
</div> : <span className="ai_home_card_tag mlat">{model.domainName}</span>}
</div>
</Fragment>}
</div>}
</ActionItem>
))}
</div> : <div className="pt80"><Nodata _html="暂无数据"/></div>}
</Spin>
{/* 分页 */}
<Pagination className="edu-txt-right ai_list_Pagination mb70 black_active_Pagination" current={page} onChange={(p) => { setPage(p) }} pageSize={limit} total={total} showQuickJumper hideOnSinglePage />
</div>
</div>
</div>
);
}
export default List;

View File

@ -1,323 +0,0 @@
.ai_home_page {
background: linear-gradient(180deg, #f8f8fe 0%, #f1f6fd 100%);
// Banner 区域
.ai_home_banner {
background-image: url('../image/bk1.png');
background-size: 100% 100%;
padding: 50px 20px 80px 20px;
.ai_home_banner_title {
// 星星装饰
.ai_home_banner_stars {
position: relative;
display: inline-block;
margin-left: 20px;
.star_1,
.star_2 {
position: absolute;
animation: twinkle 2s infinite ease-in-out;
}
.star_1 {
width: 35px;
top: -35px;
}
.star_2 {
width: 20px;
animation-delay: 0.5s;
top: -46px;
left: 38px;
}
.star_3 {
position: absolute;
top: -3px;
left: 25px;
}
}
}
// Tab 切换
.ai_home_banner_tabs {
border: 1px solid #091221;
border-radius: 24px;
display: inline-flex;
justify-content: center;
}
.ai_home_banner_tab {
font-weight: 600;
padding: 7px 36px;
&,
&:hover,
&:focus {
color: #091221;
}
i {
display: none;
color: inherit;
}
&.active {
background: #091221;
border-radius: 22px;
color: white;
i {
display: inline-block;
font-weight: normal;
}
}
&:not(:first-of-type) {
margin-left: -18px;
}
&:hover:not(.active) {
background: rgba(9, 18, 33, 0.05);
border-radius: 22px;
}
}
}
// 主体内容区域
.ai_home_content {
display: flex;
gap: 30px;
min-height: 60vh;
}
// 左侧边栏
.ai_home_sidebar {
flex: 0 0 340px;
height: fit-content;
.ai_home_sidebar_list {
.ai_home_sidebar_item {
width: fit-content;
padding: 7px 33px;
cursor: pointer;
transition: all 0.2s;
border: 1px solid rgba(22, 76, 228, 0.1);
border-radius: 22px;
&:hover {
border-color: #091221;
}
&.active {
border-color: #091221;
background: #091221;
color: #fff;
}
}
}
}
// 工具栏
.ai_home_toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
gap: 20px;
flex-wrap: wrap;
.ai_home_search {
background: linear-gradient(89.82deg, #ffffff 1.89%, #f8f9fd 99.99%);
border: 1px solid rgba(22, 76, 228, 0.1);
border-radius: 12px;
padding: 8px 15px;
.ant-input-search-button {
width: 28px;
height: 28px;
background: #091221;
border-radius: 16px;
padding: 0;
border: none !important;
}
input {
background: none !important;
border: none;
font-size: 15px;
&::placeholder {
color: #76849b;
}
}
}
.ai_home_action_my{
i{
display: inline-block;
position: relative;
top: 1px;
}
&:hover{
i{
animation: arrow-move 1.2s ease-in-out infinite;
}
}
}
}
// 模型列表
.ai_home_list {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 30px;
}
.ai_home_list.skills_pattern .ai_home_card {
&:nth-child(4n+1) {
.action-item_content {
background: linear-gradient(88.92deg, #ececff 2.11%, #f8f9fd 99.79%);
}
}
&:nth-child(4n) {
.action-item_content {
background: linear-gradient(88.92deg, #fdf5fd 2.11%, #f8f9fd 99.79%);
}
}
.action-item_content {
background: linear-gradient(88.92deg, #eef3fd 2.11%, #f8f9fd 99.79%);
}
}
.codes_pattern {
.ai_home_card {
.action-item_content {
padding: 13px 14px;
background: linear-gradient(88.92deg, #E3E9FE 2.11%, #ECF1FD 99.79%);
&>div {
padding: 20px 26px;
background: linear-gradient(90.61deg, #ffffff 1.22%, rgba(255, 255, 255, 0.44) 18.41%, rgba(255, 255, 255, 0.66) 98.18%);
border-radius: 6px;
}
}
&:nth-child(4n) .action-item_content {
background: linear-gradient(88.92deg, #eaeeff 2.11%, #F5F7FE 99.79%);
}
&:nth-child(4n+1) .action-item_content {
background: linear-gradient(88.92deg, #ebecff 2.11%, #f1f1fd 99.79%);
}
&:nth-child(4n+2) .action-item_content {
background: linear-gradient(88.92deg, #E8F4F9 2.11%, #F5F9FE 99.79%);
}
}
}
.agents_pattern{
grid-template-columns: repeat(3, 1fr);
.ai_home_card{
position: relative;
.action-item_content{
padding: 27px 36px;
background-image: url('../image/card_bk3.png');
background-size: 100% 100%;
}
&:hover{
.btn-Box{
display: block;
position: absolute;
left: 0;
bottom: 17px;
text-align: center;
width: 100%;
}
}
.btn-Box{
display: none;
}
.btn-black{
padding: 4px 48px 4px 48px;
}
.unClickBtn{
padding: 8px 47px 8px 47px;
background:#9ea2a8;
border-radius:10px;
color:#ffffff;
font-size:15px;
cursor: not-allowed;
}
}
}
// 卡片
.ai_home_card {
width: auto;
&:hover {
.btn-black {
display: inline-block;
}
}
&.action-item:hover:before {
background: conic-gradient(from 180deg at 50% 50%,
#587fee 0deg,
#9467e3 45deg,
transparent 90deg,
transparent 180deg,
#587fee 180deg,
#9467e3 225deg,
transparent 270deg);
}
.action-item_content {
border: 1px solid rgba(22, 76, 228, 0.1);
background: white url('../image/card_bk1.png');
background-blend-mode: multiply;
padding: 20px 32px;
width: auto;
border-radius: 11px;
}
.ai_home_card_tag {
background: #f5f9fd;
border: 1px solid #d3e1fd;
border-radius: 4px;
color: #091221;
font-size: 13px;
line-height: 24px;
padding: 1px 9px;
}
.icon-colora-zuixin1 {
vertical-align: middle;
}
.btn-black {
padding: 0px 19px 4px 19px;
display: none;
transition: all 0.3s;
}
}
.skills_bottom {
background: rgba(255, 255, 255, 0.7);
border-radius: 6px;
padding: 13px 16px;
margin-top: 17px;
}
.ai_list_Pagination {
margin-top: 45px;
}
}

View File

@ -1,234 +0,0 @@
import { Fragment, useEffect, useState } from "react";
import { Link } from "react-router-dom";
import { Table, Pagination, Badge, message } from "antd";
import './index.scss';
import { aiResouceStatus, aiResouceType, aiResouceTypeKey } from "../static";
import { drawSourceById, getAIProjectStatistics, getAIResourceStatistics, getMyCodeList, getMyResourceList, takeUpSourceById } from "../../forge/Information/api";
import { DeleteModal, TakeDownModal } from "../component";
import moment from "moment";
function MyResources(props) {
const pageSize = 10;
const { match: { params }, history, aiZoneInfo, roleByZone, current_user } = props || {};
const type = params.type;
const { id: aiZoneId } = aiZoneInfo || {}
const { login } = current_user || {}
const [current, setCurrent] = useState(1);
const [total, setTotal] = useState(0);
const [reload, setReload] = useState(undefined);
const [list, setList] = useState([]);
const [deleteId, setDeleteId] = useState();
const [downId, setDownId] = useState();
const [stats, setStats] = useState([]);
useEffect(() => {
document.title = `我的资源池`
}, [])
//
useEffect(() => {
if (!login) {
history.push(`/login?go_page=${encodeURIComponent(window.location.pathname)}`);
}
}, [login]);
useEffect(() => {
if (aiZoneId && type) {
if(type === "codes") {
getAIProjectStatistics({
zoneId: aiZoneId,
}).then(res => {
if (res && res.data && res.data.code === 200) {
const { likeCount, publishedCount, pendingCount } = res.data.data || {};
setStats([
{ key: 'likes', label: '累计获得点赞', value: likeCount || 0 },
{ key: 'published', label: '已发布', value: publishedCount || 0 },
{ key: 'reviewing', label: '审核中', value: pendingCount || 0 },
]);
}
})
return;
}
getAIResourceStatistics({
isMine: true,
resourceCategory: aiResouceTypeKey[type],
zoneId: aiZoneId,
}).then(res => {
if (res && res.data && res.data.code === 200) {
const { likeCount, publishedCount, pendingCount } = res.data.data || {};
setStats([
{ key: 'likes', label: '累计获得点赞', value: likeCount || 0 },
{ key: 'published', label: '已发布', value: publishedCount || 0 },
{ key: 'reviewing', label: '审核中', value: pendingCount || 0 },
]);
}
})
}
}, [aiZoneId, type, reload])
useEffect(() => {
if(type === "codes" && aiZoneId) {
//
getMyCodeList({
zoneId: aiZoneId,
pageNum: current,
pageSize: pageSize,
}, roleByZone).then(res => {
if(res && res.data && res.data.code === 200){
setList(res.data.rows);
setTotal(res.data.total)
}
});
return
}
aiZoneId && getMyResourceList({
auditStatus: "all",
zoneId: aiZoneId,
resourceCategory: aiResouceTypeKey[type],
pageNum: current,
pageSize: pageSize,
}, roleByZone).then(res => {
if(res && res.data && res.data.code === 200){
setList(res.data.rows);
setTotal(res.data.total)
}
})
}, [type, aiZoneId, current, reload, roleByZone])
const columns = [
{
title: <span className="pl25">资源名称</span>,
dataIndex: type === "codes" ? "projectProperties.name" : 'name',
key: 'name',
render: (text, record) => {
return <a href={type === "codes" ? `/${record.projectProperties && record.projectProperties.fullName}` : `/ai/${type}/${record.id}`}>{text}</a>
}
},
{
title: '提交时间',
dataIndex: 'updateTime',
key: 'updateTime',
render: (text) => {
return moment(text).format('YYYY-MM-DD HH:mm')
}
},
{
title: '状态',
key: 'auditStatus',
dataIndex: 'auditStatus',
render: (text, record) => {
const status = aiResouceStatus[text] || {};
return <Badge color={status.color} text={status.label} className='status-dot' />
},
},
{
title: '操作',
key: 'action',
className: 'col-actions',
render: (text, record) => {
const { auditStatus, id } = record;
return type === "codes" ? <a onClick={()=>{setDeleteId(id)}}>删除</a> : auditStatus === "1" ? <a onClick={()=>{setDownId(id)}}>下架</a> : auditStatus === "0" ? <a onClick={()=>{
drawSourceById(id).then(res=>{
if(res && res.data && res.data.code === 200){
message.success(`操作成功`);
setReload(Math.random());
}
})
}}>撤回</a> :
<Fragment>
{auditStatus === "3" && <a className="mr20" onClick={()=>{
takeUpSourceById(id, roleByZone).then(res=>{
if(res && res.data && res.data.code === 200){
message.success(`操作成功`);
setReload(Math.random());
}
})
}}>上架</a>}
<a className="mr20" href={`/ai/${type}/edit/${id}`}>修改</a>
<a onClick={()=>{setDeleteId(id)}}>删除</a>
</Fragment>
},
},
];
//
const handlePageChange = (page) => {
setCurrent(page);
};
return (
<div className="my-resources-page">
<div className="ai_1600 container">
{/* 左侧区域 */}
<div className="left-panel pt40">
{/* 标题区域 */}
<div className="header-section ai_banner_title">
<Link to={`/ai${type === "all" ? '' : `/${type}`}`} className="back-btn">
<i className="iconfont icon-jiantou2 font-20"></i>
</Link>
<span className="font-40">我的资源池</span>
<div className="carousel_text mt5">栈筑 AI 全能力</div>
</div>
<div className="font-15 color768 mt20">
聚合数据集模型Skill 场景能力与工程代码模块化灵活编排一站式搭建全链路智能 AI 应用
</div>
{/* 分类标签 */}
<div className="category-tabs mt30">
{aiResouceType.map((category) => (
<Link
to={`/ai/${category.key}/my`}
key={category.key}
className={`category-tab ${type === category.key ? 'active' : ''}`}
>
{category.name}
</Link>
))}
<Link
to={`/ai/${type}/create`}
className={`category-tab-add`}
>
<i className="iconfont icon-jia font-12"></i>
</Link>
</div>
{/* 统计数据 */}
<div className="stats-section mt60">
{stats.map((stat) => (
<div key={stat.key} className="stat-card">
<span>{stat.value}</span>
<div className="font-15">{stat.label}</div>
</div>
))}
</div>
</div>
{/* 右侧区域 */}
<div className="right-panel">
<div className="table-container">
<Table
columns={columns}
dataSource={list}
rowKey="id"
pagination={false}
/>
{/* 分页 */}
<Pagination
current={current}
pageSize={pageSize}
total={total}
onChange={handlePageChange}
showSizeChanger={false}
showTotal={(total) => `${total} 条记录`}
className="black_active_Pagination edu-txt-right mt36"
/>
</div>
</div>
</div>
<DeleteModal type={type} id={deleteId} cancel={()=>{setDeleteId(undefined)}} roleByZone={roleByZone} reload={setReload}/>
<TakeDownModal id={downId} cancel={()=>{setDownId(undefined)}} roleByZone={roleByZone} reload={setReload}/>
</div>
);
}
export default MyResources;

View File

@ -1,165 +0,0 @@
.my-resources-page {
background-image: url('../image/bk5.png');
background-size: 100% auto;
background-repeat: no-repeat;
padding-top: 55px;
min-height: 91vh;
.container {
display: flex;
gap: 75px;
}
// 左侧区域
.left-panel {
flex: 0 0 400px;
height: fit-content;
// 返回按钮
.back-btn {
display: inline-block;
transform: scaleX(-1) translateY(-7px);
margin-right: 12px;
}
// 标题区域
.header-section {
line-height: normal;
}
// 分类标签
.category-tabs {
display: flex;
align-items: center;
margin-bottom: 30px;
border: 1px solid #091221;
border-radius: 24px;
.category-tab {
flex: 1;
height: 44px;
line-height: 44px;
border-radius: 22px;
cursor: pointer;
transition: all 0.2s;
text-align: center;
&:hover {
color: inherit;
background-color: rgba(9, 18, 33, 0.05);
}
&.active {
background: #091221;
color: #fff;
}
}
.category-tab-add{
width:34px;
height:34px;
line-height: 34px;
text-align: center;
background:#091221;
border-radius:17px;
color: white;
margin-right: 9px;
}
}
// 统计数据
.stats-section {
display: flex;
.stat-card {
margin-right: auto;
span {
font-size: 44px;
font-family: "YouSheBiaoTiHei";
line-height: normal;
}
}
}
}
// 右侧区域
.right-panel {
flex: 1;
width: 0;
background-image: url('../image/bk4.png');
background-size: 100% 100%;
padding: 20px;
border: 2px solid #ffffff;
border-radius: 12px;
.table-container .ant-table {
min-height: 650px;
background: white;
border: 1px solid;
border-color: rgba(22, 76, 228, 0.1);
border-radius: 12px;
overflow: hidden;
table {
color: #091221;
font-size: 15px;
}
.ant-table-tbody>tr>td {
border-bottom: none
}
.ant-table-tbody>tr {
background: linear-gradient(89.78deg, rgba(236, 236, 255, 0.58) 1.89%, rgba(248, 249, 253, 0) 99.99%);
&:nth-child(2n) {
background: linear-gradient(89.78deg, #eef3fd 1.89%, rgba(248, 249, 253, 0) 99.99%);
}
td {
border-top: 25px solid white;
&:first-of-type {
border-left: 12px solid white;
font-size: 16px;
font-weight: bold;
}
}
}
.ant-table-thead>tr>th {
background: #f5f6ff;
color: #091221;
font-size: 15px;
font-weight: bold;
border-bottom-color: rgba(22, 76, 228, 0.1);
}
}
.status-dot {
.ant-badge-status-text {
color: #091221;
font-size: 15px;
}
.ant-badge-status-dot {
&::before {
content: '';
position: absolute;
top: -2px;
left: -2px;
width: 10px;
height: 10px;
background-color: inherit;
z-index: 1;
opacity: 0.2;
border-radius: 50%;
}
}
}
.col-actions a{
color:#164ce4;
}
}
}

View File

@ -1,140 +0,0 @@
import { Fragment } from "react";
export const aiResouceType = [
{ key: "models", name: "模型库", icon: "moxingku", intro: "行业算法模型", desc: "收录多模态原生大模型矩阵,开箱即用灵活调度,全方位支撑企业 AI 业务研发与智能化升级", createIntro: "上传您的模型文件审核通过后将同步展示在AI广场" },
{ key: "datasets", name: "数据集", icon: "shujuji", intro: "高精训练数据", desc: "精心策划的多模态数据集,覆盖 NLP、CV、语音、多模态等核心方向助力模型快速迭代。", createIntro: "上传您的数据集文件审核通过后将同步展示在AI广场" },
{ key: "skills", name: "Skills", icon: "skill", intro: "场景智能能力", desc: "标准化封装多维场景 Skill 能力,灵活组合调用,低成本快速搭建各类智能化 AI 业务应用。", createIntro: "上传您的skill文件审核通过后将同步展示在AI广场" },
{ key: "codes", name: "代码库", icon: "daimaku1", intro: "通用开发源码", desc: "模块化沉淀全场景通用代码能力,积木式灵活编排复用,高效轻量化落地各类研发业务需求", createIntro: "上传您的代码库审核通过后将同步展示在AI广场" }
]
export const aiResouceTypeKey = {
models: "MODEL",
datasets: "DATASET",
skills: "SKILL"
}
// 已发布:审核通过/上架
export const aiResouceStatus = {
"0": {label: "待审核", color: "#ff9140"},
"1": {label: "已发布", color: "#164ce4"},
"2": {label: "已驳回", color: "#d82944"},
"3": {label: "已下架", color: "#5C5A59"},
"4": {label: "草稿", color: "#ff5e00"},
}
// 步骤引导数据
export const aiCreateSteps = [
{
title: "注册/登录账号",
description: "首先需要注册或登录您的账号,才能开始发布资源"
},
{
title: "选择资源类型",
description: "选择您要发布的资源类型模型、数据集、Skill 或代码库"
},
{
title: "填写信息并发布",
description: "填写资源的详细信息,包括名称、简介、标签等,然后提交发布"
}
];
// 格式化文件大小
const formatFileSize = (size) => {
if (!size || size === 0) return "";
const num = parseFloat(size);
if (isNaN(num)) return "";
const units = ["B", "KB", "MB", "GB", "TB"];
let unitIndex = 0;
let fileSize = num;
while (fileSize >= 1024 && unitIndex < units.length - 1) {
fileSize /= 1024;
unitIndex++;
}
const formatted = fileSize.toFixed(2);
// 如果两位小数都是0则显示整数
return formatted.endsWith('.00') ? `${parseInt(formatted, 10)}${units[unitIndex]}` : `${formatted}${units[unitIndex]}`;
};
export const convertToTreeData = (data) => {
return data.map(node => {
const isLeaf = !node.children || node.children.length === 0;
let title = node.name;
if (isLeaf && node.size) {
const sizeStr = formatFileSize(node.size);
title = <Fragment><span>{node.name}</span><span className="fileSize">{sizeStr}</span></Fragment>;
}
return {
key: node.name,
title,
isLeaf,
size: isLeaf ? node.size : undefined,
children: node.children && node.children.length > 0 ? convertToTreeData(node.children) : undefined
};
});
};
// 分类图标
export const iconByCateName = {
"开发工具": ["iconfont-color icon-colora-zu18240", "iconfont icon-a-Dshujukukaifagongju"],
"多模态": ["iconfont-color icon-colora-zu18210", "iconfont icon-duomotaimoxing"],
"自然语言处理": ["iconfont-color icon-colorziranyuyanchuli", "iconfont icon-ziranyuyanchuli-2"],
"语音": ["iconfont-color icon-coloryuyin", "iconfont icon-yuyin-1"],
"计算机视觉": ["iconfont-color icon-colora-zu18213", "iconfont icon-a-zu18214"],
"科学计算": ["iconfont-color icon-colorkexueyanjiuhejishufuwuye", "iconfont icon-a-kexueyanjiuhejishufuwuye-1"],
"文本": ["iconfont-color icon-colorziranyuyanchuli", "iconfont icon-ziranyuyanchuli-2"],
"图像": ["iconfont-color icon-colortuxiang", "iconfont icon-a-tuxiang-1"],
"音频": ["iconfont-color icon-coloryinpin", "iconfont icon-yinpin-1"],
"视频": ["iconfont-color icon-colorshipinbofang", "iconfont icon-shipinbofang-1"],
"开发工具": ["iconfont-color icon-colora-zu18240", "iconfont icon-a-Dshujukukaifagongju"],
"市场推广": ["iconfont-color icon-colora-zu18213", "iconfont icon-a-zu18214"],
"前端开发": ["iconfont-color icon-colordaimaku1", "iconfont icon-daimaku1"],
"媒体处理": ["iconfont-color icon-coloryouxiyule", "iconfont icon-meitichuli"],
"代码质检": ["iconfont-color icon-colordaimazhijian", "iconfont icon-daimazhijian"],
"移动开发": ["iconfont-color icon-coloryidongkaifa", "iconfont icon-yidongkaifa"],
"云效工具": ["iconfont-color icon-coloryunxiaogongju", "iconfont icon-yunxiaogongju"],
"其他": ["iconfont-color icon-colorqita", "iconfont icon-qita"],
"程序开发": ["iconfont-color icon-colora-zu18240", "iconfont icon-a-Dshujukukaifagongju"],
"操作系统": ["iconfont-color icon-colora-zu18213", "iconfont icon-a-zu18214"],
"人工智能": ["iconfont-color icon-colordaimaku1", "iconfont icon-daimaku1"],
"游戏娱乐": ["iconfont-color icon-coloryouxiyule", "iconfont icon-meitichuli"],
"程序设计": ["iconfont-color icon-colordaimazhijian", "iconfont icon-daimazhijian"],
"云原生": ["iconfont-color ", "iconfont "]
}
export const iconByCateIndex = {
"models": [
["iconfont-color icon-colora-zu18210", "iconfont icon-duomotaimoxing"],
["iconfont-color icon-colorziranyuyanchuli", "iconfont icon-ziranyuyanchuli-2"],
["iconfont-color icon-coloryuyin", "iconfont icon-yuyin-1"],
["iconfont-color icon-colora-zu18213", "iconfont icon-a-zu18214"],
["iconfont-color icon-colorkexueyanjiuhejishufuwuye", "iconfont icon-a-kexueyanjiuhejishufuwuye-1"]
],
"datasets": [
["iconfont-color icon-colora-zu18210", "iconfont icon-duomotaimoxing"],
["iconfont-color icon-colorziranyuyanchuli", "iconfont icon-ziranyuyanchuli-2"],
["iconfont-color icon-colortuxiang", "iconfont icon-a-tuxiang-1"],
["iconfont-color icon-coloryinpin", "iconfont icon-yinpin-1"],
["iconfont-color icon-colorkexueyanjiuhejishufuwuye", "iconfont icon-a-kexueyanjiuhejishufuwuye-1"],
["iconfont-color icon-colorshipinbofang", "iconfont icon-shipinbofang-1"]
],
"skills": [
["iconfont-color icon-colora-zu18240", "iconfont icon-a-Dshujukukaifagongju"],
["iconfont-color icon-colora-zu18213", "iconfont icon-a-zu18214"],
["iconfont-color icon-colordaimaku1", "iconfont icon-daimaku1"],
["iconfont-color icon-coloryouxiyule", "iconfont icon-meitichuli"],
["iconfont-color icon-colordaimazhijian", "iconfont icon-daimazhijian"],
["iconfont-color icon-coloryidongkaifa", "iconfont icon-yidongkaifa"],
["iconfont-color icon-coloryunxiaogongju", "iconfont icon-yunxiaogongju"],
["iconfont-color icon-colorqita", "iconfont icon-qita"]
],
"codes": [
["iconfont-color icon-colora-zu18240", "iconfont icon-a-Dshujukukaifagongju"],
["iconfont-color icon-colora-zu18213", "iconfont icon-a-zu18214"],
["iconfont-color icon-colordaimaku1", "iconfont icon-daimaku1"],
["iconfont-color icon-coloryouxiyule", "iconfont icon-meitichuli"],
["iconfont-color icon-colordaimazhijian", "iconfont icon-daimazhijian"],
["iconfont-color icon-coloryidongkaifa", "iconfont icon-yidongkaifa"],
["iconfont-color icon-coloryunxiaogongju", "iconfont icon-yunxiaogongju"],
["iconfont-color icon-colorqita", "iconfont icon-qita"]
]
}

View File

@ -1,74 +0,0 @@
@keyframes itemUp {
0% {
top: var(--offsetTop)
}
to {
top: var(--top, 0px)
}
}
@keyframes bgOpacity {
0% {
opacity: 1
}
to {
opacity: 0
}
}
@keyframes voiceScale {
0% {
transform: scale(0)
}
58% {
transform: scale(1.28)
}
to {
transform: scale(1)
}
}
@keyframes voiceRotate {
0% {
transform: rotate(0)
}
55% {
transform: rotate(6deg)
}
to {
transform: scale(-4deg)
}
}
@keyframes scaleTo1 {
to {
transform: scale(1);
}
}
// 左右摆动动画
@keyframes arrow-move {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(4px); }
}
// 星星闪烁动画
@keyframes twinkle {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.7;
transform: scale(0.9);
}
}

View File

@ -9,11 +9,15 @@ if (!__SERVER__) {
}
const isDev = __SERVER__ ? false : window.location.port == 3007;
const isdev2= __SERVER__ ? false : window.location.hostname ==='www.educoder.net'
export const TEST_HOST = "https://testforgeplus.trustie.net/"
export const TEST_HOST = "http://172.20.32.202:4000/"
export function getImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
const local = 'https://testforgeplus.trustie.net';
if (isDev) {
return `${TEST_HOST}${path}`
return `${local}${path}`
}
return `${path}`;
}
@ -34,10 +38,14 @@ export function isPhone(){
}
export function getImageUrlAbsolute(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
path && !path.startsWith('/') && !path.startsWith('http') && (path = '/'.concat(path));
const prod = __SERVER__ ? TEST_HOST : window.location.origin;
const local = 'http://172.20.32.202:4000/';
const prod = __SERVER__ ? local : window.location.origin;
if (isDev) {
return `${TEST_HOST}${path}`
return `${local}${path}`
}else{
return `${prod}${path}`;
}
@ -61,9 +69,13 @@ export function numFormat(num, digits){
}
export function getImage(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const local = 'http://172.20.32.202:4000//';
if(path.indexOf("http://")===-1){
if (isDev) {
return `${TEST_HOST}/images/${path}`
return `${local}/images/${path}`
}
return `/${path}`;
}else{
@ -72,9 +84,14 @@ export function getImage(path) {
}
export function getcdnImageUrl(path) {
// https://www.educoder.net
// https://testbdweb.trustie.net
// const local = 'http://localhost:3000'
const testlocal = 'https://testali-cdn.educoder.net'
const local='https://ali-cdn.educoder.net'
let firstStr=path.substr(0,1);
if (isdev2) {
return `${TEST_HOST}/${path}`;
return `${local}/${path}`;
}else{
if(firstStr=="/"){
return `${path}`
@ -86,18 +103,20 @@ export function getcdnImageUrl(path) {
}
export function setcndImagesUrl(path){
const testlocal = 'https://testali-cdn.educoder.net'
const local='https://ali-cdn.educoder.net'
let firstStr=path.substr(0,1);
if(firstStr=="/"){
if (!isdev2) {
return `${path}`
}
return `${TEST_HOST}${path}`;
return `${local}${path}`;
//return isDev?`${testlocal}${path}`:`${local}${path}`;
}else{
if (!isdev2) {
return `/${path}`
}
return `${TEST_HOST}/${path}`;
return `${local}/${path}`;
// return isDev?`${testlocal}/${path}`:`${local}/${path}`;
}
}
@ -124,10 +143,10 @@ export function setImagesUrl(path){
}
export function getUrl(path, goTest) {
const local = 'http://172.20.32.203:4000'
if (isDev) {
return `${local}${path?path:''}`
}
// const local = 'http://172.20.32.202:8080'
// if (isDev) {
// return `${local}${path?path:''}`
// }
return `${path ? path: ''}`;
}
@ -136,7 +155,7 @@ export function getZoneUrl(path) {
let settings = localStorage.chromesetting&&JSON.parse(localStorage.chromesetting);
let actionUrl = settings && settings.common.zone;
return `${actionUrl}/zoneAdmin${path?path:''}`
return `${actionUrl}${path?path:''}`
}
export function getUrlmys(path, goTest) {
@ -358,13 +377,13 @@ export function setDefaultMeta() {
if (__SERVER__) {
document = domObj.window.document
}
document.querySelector(`title`).textContent = '菌队可控开源社区 | 新一代开源创新服务平台';
document.querySelector(`meta[property='og:title']`).content = '菌队可控开源社区';
document.querySelector(`title`).textContent = '生态治理平台 | 新一代开源创新服务平台';
document.querySelector(`meta[property='og:title']`).content = '生态治理平台';
document.querySelector(`meta[property='og:url']`).content = 'https://GitLink.org.cn/';
document.querySelector(`meta[property='og:description']`).content = '菌队可控开源社区 是一个新一代的开源创新服务平台,提供分布式协作开发、流水线运维、代码分析等功能,基于 Git 打造分布式代码托管环境。';
document.querySelector(`meta[property='og:image:alt']`).content = '菌队可控开源社区';
document.querySelector('meta[name="Keywords"]').content= '菌队可控开源社区, git, 开源, 代码托管, 项目管理, 版本控制, 代码分享, 项目协作, 协同开发平台';
document.querySelector(`meta[name='description']`).content = '菌队可控开源社区 是一个新一代的开源创新服务平台,提供分布式协作开发、流水线运维、代码分析等功能,基于 Git 打造分布式代码托管环境。';
document.querySelector(`meta[property='og:description']`).content = '生态治理平台 是一个新一代的开源创新服务平台,提供分布式协作开发、流水线运维、代码分析等功能,基于 Git 打造分布式代码托管环境。';
document.querySelector(`meta[property='og:image:alt']`).content = '生态治理平台';
document.querySelector('meta[name="Keywords"]').content= '生态治理平台, git, 开源, 代码托管, 项目管理, 版本控制, 代码分享, 项目协作, 协同开发平台';
document.querySelector(`meta[name='description']`).content = '生态治理平台 是一个新一代的开源创新服务平台,提供分布式协作开发、流水线运维、代码分析等功能,基于 Git 打造分布式代码托管环境。';
document.querySelector(`meta[name='go-import']`).content ='GitLink.org.cn git https://GitLink.org.cn';
document.querySelector(`meta[name='octolytics-dimension-user_login']`).content = 'GitLink';
document.querySelector(`meta[name='octolytics-dimension-repository_nwo']`).content = 'GitLink';

View File

@ -4,7 +4,7 @@ const wx = window.wx
const appId = 'wxf8debe756b4a0309'
let settings = localStorage && localStorage.chromesetting && localStorage.chromesetting !== "undefined" && JSON.parse(localStorage.chromesetting);
let actionUrl = settings && settings.common.zone +'/zoneAdmin/api';
let actionUrl = settings && settings.common.zone +'/api';
const service = axios.create({
baseURL: actionUrl,
@ -128,8 +128,8 @@ export function configShareForCustom (title, desc, imgUrl, path) {
console.log('configShareForCustom', host)
const _url = window.location.href.split('#')[0];
var shareData = {
title: title || '菌队可控开源社区',
desc: desc || '菌队可控开源社区,新一代开源创新服务平台',
title: title || '生态治理平台',
desc: desc || '生态治理平台,新一代开源创新服务平台',
link: path ? `${host}/${path}` : _url,
imgUrl: imgUrl || host + '/build/images/share_logo_icon.png'
};

View File

@ -1,849 +0,0 @@
import React, { useState, useRef, useCallback, useEffect, useImperativeHandle, forwardRef } from 'react';
import { Button, Progress, message, Spin } from 'antd';
import SparkMD5 from 'spark-md5';
import './index.scss';
/**
* 分片上传组件
*
* @param {Object} props
* @param {Function} props.onUploadComplete - 上传完成回调 (fileInfo) => void
* @param {Function} props.onUploadProgress - 上传进度回调 (progress, uploaded, total) => void
* @param {Function} props.onUploadError - 上传错误回调 (error) => void
* @param {Function} props.onCancel - 取消上传回调 () => void
* @param {string} props.fileType - 文件类型 (cms/dms/resource/pms等)
* @param {string} props.hierarchy - 层级结构
* @param {number} props.chunkSize - 分片大小字节
* @param {boolean} props.disabled - 是否禁用
* @param {string} props.accept - 接受的文件类型
* @param {number} props.maxSize - 最大文件大小字节
* @param {Object} props.defaultFile - 默认文件信息已上传的文件用于回显
* - {string} defaultFile.fileId - 文件ID
* - {string} defaultFile.fileOriginName - 文件名
* - {number} defaultFile.fileSize - 文件大小字节
* - {string} defaultFile.url - 文件下载地址
*/
/**
* 暴露给父组件的方法通过 ref
*
* @method cancelUpload - 移除上传的文件重置为待上传状态
*/
const ChunkedUpload = forwardRef(({
onUploadComplete,
onUploadProgress,
onUploadError,
onCancel,
fileType = 'resource',
hierarchy = '',
chunkSize = 10 * 1024 * 1024, // 10MB
disabled = false,
accept = '', // HTML5
maxSize = 1024 * 1024 * 1024, // 1GB
children, //
defaultFile, //
}, ref) => {
//
useImperativeHandle(ref, () => ({
/**
* 移除上传的文件重置为待上传状态
*/
cancelUpload: () => {
handleCancel();
},
}));
const DEFAULT_SIZE = 10 * 1024 * 1024; // 10MB ruoyi-react
const [uploading, setUploading] = useState(false);
const [file, setFile] = useState(null);
const [uploadStatus, setUploadStatus] = useState('idle'); // idle, checking, initializing, uploading, merging, completed, error, cancelled, instant
const [progress, setProgress] = useState(0);
const [uploadedChunks, setUploadedChunks] = useState(0);
const [totalChunks, setTotalChunks] = useState(0);
const [uploadId, setUploadId] = useState(null);
const [errorMessage, setErrorMessage] = useState('');
const [uploadSpeed, setUploadSpeed] = useState(0);
const [instantFileInfo, setInstantFileInfo] = useState(null); //
const fileInputRef = useRef(null);
//
useEffect(() => {
if (defaultFile && Object.keys(defaultFile).length > 0) {
//
const fileInfo = {
name: defaultFile.fileOriginName || 'unknown',
size: defaultFile.fileSize || 0,
};
setFile(fileInfo);
setUploadStatus('completed');
setProgress(100);
setUploadedChunks(1);
setTotalChunks(1);
}
}, [defaultFile]);
const uploadRef = useRef({
isCancelled: false,
uploadId: null,
abortController: null,
currentFile: null,
fileSignature: null, //
});
//
const cleanupUpload = useCallback(() => {
uploadRef.current.isCancelled = true;
//
if (uploadRef.current.abortController) {
uploadRef.current.abortController.abort();
}
setUploadStatus('cancelled');
setUploading(false);
}, []);
//
const handleCancel = useCallback(() => {
uploadRef.current.isCancelled = true;
//
if (uploadRef.current.abortController) {
uploadRef.current.abortController.abort();
}
setUploadStatus('idle');
setUploading(false);
setUploadId(null);
setFile(null);
setProgress(0);
setUploadedChunks(0);
setTotalChunks(0);
setErrorMessage('');
onCancel?.();
}, [onCancel]);
//
const formatFileSize = useCallback((bytes) => {
if (!bytes || bytes === 0) return '0 B';
if(typeof bytes === "string") return bytes
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}, []);
//
const formatSpeed = useCallback((bytesPerSecond) => {
return formatFileSize(bytesPerSecond) + '/s';
}, [formatFileSize]);
//
const updateProgress = useCallback((uploaded, total, fileSize, elapsedMs) => {
const percent = Math.round((uploaded / total) * 100);
setProgress(percent);
setUploadedChunks(uploaded);
setTotalChunks(total);
//
const speed = elapsedMs > 0 ? Math.round((uploaded * chunkSize) / (elapsedMs / 1000)) : 0;
setUploadSpeed(speed);
onUploadProgress?.(percent, uploaded, total);
}, [chunkSize, onUploadProgress]);
//
const uploadSingleChunk = useCallback(async (chunkNumber, chunkFile, onChunkProgress) => {
const { uploadChunk: uploadChunkApi } = await import('../../factory/api');
let retryCount = 0;
const maxRetries = 3;
while (retryCount < maxRetries) {
try {
const abortController = new AbortController();
uploadRef.current.abortController = abortController;
await uploadChunkApi(
{
uploadId: uploadRef.current.uploadId,
chunkNumber,
chunk: chunkFile,
},
onChunkProgress
);
return true;
} catch (error) {
if (error.name === 'AbortError') {
return false;
}
retryCount++;
if (retryCount >= maxRetries) {
console.error(`分片 ${chunkNumber} 上传失败,已达最大重试次数:`, error);
throw error;
}
//
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
}
}
return false;
}, []);
//
const uploadChunksConcurrently = useCallback(async (chunks, fileToUpload) => {
const uploaded = new Set();
const chunksToUpload = chunks.filter(c => !uploaded.has(c.index));
if (chunksToUpload.length === 0) {
return;
}
const startTime = Date.now();
let uploadedCount = 0;
const maxConcurrent = 4;
//
const processChunk = async (chunk) => {
if (uploadRef.current.isCancelled) return;
try {
const start = chunk.start;
const end = chunk.end;
const blob = fileToUpload.slice(start, end);
// Blob File ruoyi-react
const chunkFile = new File([blob], `chunk-${chunk.index}-${Date.now()}`);
await uploadSingleChunk(chunk.index, chunkFile, (loaded, total) => {
//
});
uploaded.add(chunk.index);
uploadedCount++;
updateProgress(uploadedCount, chunks.length, fileToUpload.size, Date.now() - startTime);
} catch (error) {
if (error.name !== 'AbortError') {
setErrorMessage(`分片 ${chunk.index} 上传失败: ${error.message}`);
setUploadStatus('error');
onUploadError?.(error);
}
}
};
//
for (let i = 0; i < chunksToUpload.length; i += maxConcurrent) {
if (uploadRef.current.isCancelled) break;
const batch = chunksToUpload.slice(i, i + maxConcurrent);
await Promise.all(batch.map(processChunk));
}
}, [uploadSingleChunk, updateProgress, onUploadError]);
// ruoyi-react
const initUpload = useCallback(async (fileToUpload) => {
setUploadStatus('initializing');
try {
const { initChunkedUpload, checkInstantUpload } = await import('../../factory/api');
// 1.
const initResponse = await initChunkedUpload({
fileName: fileToUpload.name,
fileSize: fileToUpload.size,
type: fileType,
hierarchy: hierarchy || undefined,
});
const { data: initData } = initResponse.data || initResponse;
const { uploadId: newUploadId, totalChunks, chunkSize: serverChunkSize, hashType, sampleChunks } = initData;
uploadRef.current.uploadId = newUploadId;
setUploadId(newUploadId);
// 2. hashType sampleChunks ruoyi-react
setUploadStatus('calculating');
const fileSignature = await computeFileSignature(fileToUpload, hashType, sampleChunks, serverChunkSize || chunkSize);
uploadRef.current.fileSignature = fileSignature;
// 3. / ruoyi-react
setUploadStatus('checking');
const checkResponse = await checkInstantUpload({
uploadId: String(newUploadId),
fileSignature: fileSignature,
});
const { data: checkData } = checkResponse.data || checkResponse;
const { canInstantUpload, canResumingUpload, completedUploadChunkedNumberList, existingFileId, existingFileName, existingFileSize, existingFileUrl } = checkData || {};
// ruoyi-react
if (canInstantUpload) {
setUploadStatus('instant');
const fileInfo = {
fileId: existingFileId,
fileOriginName: existingFileName,
fileSize: existingFileSize,
url: existingFileUrl,
};
setInstantFileInfo(fileInfo);
message.success('秒传成功,文件已存在');
onUploadComplete?.(fileInfo);
setUploading(false); // uploading Spin
return null; // null
}
// ruoyi-react
let uploadedChunksSet = new Set();
if (canResumingUpload && completedUploadChunkedNumberList && completedUploadChunkedNumberList.length > 0) {
const resumingUploadId = checkData.resumingUploadId || newUploadId;
uploadRef.current.uploadId = resumingUploadId;
setUploadId(resumingUploadId);
// ruoyi-react
if (checkData.resumingUploadStatus === 'MERGING' || checkData.resumingUploadStatus === '合并中') {
// message.info('...');
//
const mergeResult = await pollMergeStatus(resumingUploadId, (mergeProgress) => {
setProgress(mergeProgress);
});
setUploadStatus('completed');
setProgress(100);
setUploading(false);
message.success('文件上传完成');
// fileId
onUploadComplete?.({
fileId: mergeResult?.fileId || mergeResult?.data?.fileId,
fileOriginName: mergeResult?.fileOriginName || mergeResult?.data?.fileOriginName,
fileSize: mergeResult?.fileSize || mergeResult?.data?.fileSize,
url: mergeResult?.url || mergeResult?.data?.url,
uploadId: newUploadId,
});
return null;
}
// Set便
uploadedChunksSet = new Set(completedUploadChunkedNumberList);
const uploadedCount = uploadedChunksSet.size;
setUploadedChunks(uploadedCount);
setTotalChunks(totalChunks);
// message.info(` ${uploadedCount} ${uploadedCount + 1} `);
//
if (uploadedCount >= totalChunks) {
const mergeResult = await mergeUpload();
setUploadStatus('completed');
setProgress(100);
setUploading(false);
message.success('上传完成');
// fileId
onUploadComplete?.({
fileId: mergeResult?.fileId || mergeResult?.data?.fileId,
fileOriginName: mergeResult?.fileOriginName || mergeResult?.data?.fileOriginName,
fileSize: mergeResult?.fileSize || mergeResult?.data?.fileSize,
url: mergeResult?.url || mergeResult?.data?.url,
uploadId: uploadRef.current.uploadId,
});
return null;
}
}
setTotalChunks(totalChunks);
//
const finalChunkSize = serverChunkSize || chunkSize;
const chunks = [];
for (let i = 0; i < totalChunks; i++) {
//
if (uploadedChunksSet.has(i)) {
continue;
}
chunks.push({
index: i, // 0
start: i * finalChunkSize,
end: Math.min((i + 1) * finalChunkSize, fileToUpload.size),
size: Math.min(fileToUpload.size - (i * finalChunkSize), finalChunkSize),
});
}
// message.info(` ${chunks.length} `);
return chunks;
} catch (error) {
setErrorMessage(`初始化上传失败: ${error.message}`);
setUploadStatus('error');
onUploadError?.(error);
throw error;
}
}, [fileType, hierarchy, chunkSize, onUploadComplete, onUploadError]);
//
const pollMergeStatus = useCallback(async (uploadId, onMergeProgress, maxRetries = 60, interval = 3000) => {
const { getProgress: getProgressApi } = await import('../../factory/api');
let retryCount = 0;
const checkStatus = async () => {
try {
const response = await getProgressApi(uploadId);
const { data: progressData } = response.data || response;
//
if (progressData.status === 'COMPLETED' || progressData.status === 'SUCCESS') {
//
onMergeProgress?.(100);
return progressData;
} else if (progressData.status === 'FAILED' || progressData.status === 'ERROR') {
//
throw new Error(progressData.message || '合并失败');
}
//
retryCount++;
//
// if (retryCount >= maxRetries) {
// throw new Error('');
// }
//
await new Promise(resolve => setTimeout(resolve, interval));
return await checkStatus();
} catch (error) {
throw error;
}
};
//
return checkStatus();
}, []);
//
const mergeUpload = useCallback(async () => {
setUploadStatus('merging');
try {
const { mergeChunks: mergeChunksApi } = await import('../../factory/api');
// 1.
const response = await mergeChunksApi(uploadRef.current.uploadId);
const { data: mergeData } = response.data || response;
//
if (mergeData?.message && mergeData.message.includes('请通过进度接口查询结果')) {
// message.info('...');
// 2.
const result = await pollMergeStatus(uploadRef.current.uploadId, (mergeProgress) => {
setProgress(mergeProgress);
});
return result;
}
//
return mergeData;
} catch (error) {
setErrorMessage(`合并分片失败: ${error.message}`);
setUploadStatus('error');
onUploadError?.(error);
throw error;
}
}, [onUploadError, pollMergeStatus]);
//
const startUpload = useCallback(async (selectedFile) => {
if (disabled) return;
//
if (selectedFile.size > maxSize) {
message.error(`文件大小不能超过 ${formatFileSize(maxSize)}`);
return;
}
setFile(selectedFile);
uploadRef.current.currentFile = selectedFile;
setUploading(true);
setUploadStatus('checking');
setProgress(0);
setUploadedChunks(0);
setErrorMessage('');
setInstantFileInfo(null);
uploadRef.current.isCancelled = false;
try {
// 1.
const chunks = await initUpload(selectedFile);
// chunks null
if (chunks === null) {
return;
}
// 2.
setUploadStatus('uploading');
await uploadChunksConcurrently(chunks, selectedFile);
if (uploadRef.current.isCancelled) {
return;
}
// 3.
const mergeResult = await mergeUpload();
setUploadStatus('completed');
setProgress(100);
setUploading(false); // uploading Spin
message.success('上传成功');
// fileId
onUploadComplete?.({
fileId: mergeResult?.fileId || mergeResult?.data?.fileId,
fileOriginName: mergeResult?.fileOriginName || mergeResult?.data?.fileOriginName,
fileSize: mergeResult?.fileSize || mergeResult?.data?.fileSize,
url: mergeResult?.url || mergeResult?.data?.url,
uploadId: uploadRef.current.uploadId,
});
} catch (error) {
if (!uploadRef.current.isCancelled) {
setUploadStatus('error');
setUploading(false); // uploading Spin
onUploadError?.(error);
}
}
}, [disabled, maxSize, formatFileSize, onUploadComplete, onUploadError, initUpload, uploadChunksConcurrently, mergeUpload]);
// ruoyi-react
const computeFileSignature = useCallback((fileToUpload, hashType = 'FULL', sampleChunks = [], chunkSize = DEFAULT_SIZE) => {
return new Promise((resolve) => {
try {
if (hashType === 'FULL') {
// MD5
computeFullMD5(fileToUpload).then(resolve);
} else if (hashType === 'SAMPLE' && sampleChunks.length > 0) {
//
computeSampleHash(fileToUpload, sampleChunks, chunkSize).then(resolve);
} else {
// 使UUID
resolve(generateUUID());
}
} catch (error) {
resolve(generateUUID());
}
});
}, []);
// MD5
const computeFullMD5 = useCallback((fileToUpload) => {
return new Promise((resolve) => {
try {
const fileReader = new FileReader();
const spark = new SparkMD5.ArrayBuffer();
fileReader.onload = (e) => {
if (e.target?.result) {
spark.append(e.target.result);
const md5 = spark.end();
resolve(md5);
} else {
resolve(generateUUID());
}
};
fileReader.onerror = function () {
resolve(generateUUID());
};
fileReader.readAsArrayBuffer(fileToUpload);
} catch (error) {
resolve(generateUUID());
}
});
}, []);
// ruoyi-react
const computeSampleHash = useCallback((fileToUpload, sampleChunks, chunkSize = DEFAULT_SIZE) => {
return new Promise((resolve) => {
try {
//
const sortedChunks = [...sampleChunks].sort((a, b) => a - b);
const totalChunks = Math.ceil(fileToUpload.size / chunkSize);
if (sortedChunks.length === 0) {
resolve(generateUUID());
return;
}
// sortedChunksMD5
const chunkMd5s = new Array(sortedChunks.length);
let processedCount = 0;
sortedChunks.forEach((chunkIndex, idx) => {
if (chunkIndex >= totalChunks) {
//
chunkMd5s[idx] = 'error';
processedCount++;
if (processedCount === sortedChunks.length) {
calculateFinalSignature(chunkMd5s, resolve);
}
return;
}
const start = chunkIndex * chunkSize;
const end = Math.min(start + chunkSize, fileToUpload.size);
const blob = fileToUpload.slice(start, end);
const fileReader = new FileReader();
fileReader.onload = (e) => {
try {
if (e.target?.result) {
// MD5
const spark = new SparkMD5.ArrayBuffer();
spark.append(e.target.result);
const chunkMd5 = spark.end();
chunkMd5s[idx] = chunkMd5;
}
} catch (error) {
chunkMd5s[idx] = 'error';
}
processedCount++;
if (processedCount === sortedChunks.length) {
//
calculateFinalSignature(chunkMd5s, resolve);
}
};
fileReader.onerror = function () {
chunkMd5s[idx] = 'error';
processedCount++;
if (processedCount === sortedChunks.length) {
calculateFinalSignature(chunkMd5s, resolve);
}
};
fileReader.readAsArrayBuffer(blob);
});
//
function calculateFinalSignature(md5List, resolveCallback) {
try {
const spark = new SparkMD5.ArrayBuffer();
const md5String = md5List.join('');
const encoder = new TextEncoder();
spark.append(encoder.encode(md5String));
const finalSignature = spark.end();
resolveCallback(finalSignature);
} catch (error) {
resolveCallback(generateUUID());
}
}
} catch (error) {
resolve(generateUUID());
}
});
}, []);
// UUID
const generateUUID = useCallback(() => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = (Math.random() * 16) | 0;
const v = c === 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}, []);
//
const handleFileSelect = useCallback((e) => {
const selectedFile = e.target.files?.[0];
if (selectedFile) {
startUpload(selectedFile);
}
}, [startUpload]);
//
const handleDrop = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
const droppedFile = e.dataTransfer?.files?.[0];
if (droppedFile) {
startUpload(droppedFile);
}
}, [startUpload]);
const handleDragOver = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
}, []);
//
const handleRetry = useCallback(() => {
setUploadStatus('checking');
setProgress(0);
setUploadedChunks(0);
setErrorMessage('');
const fileToRetry = file || uploadRef.current.currentFile;
if (fileToRetry) {
startUpload(fileToRetry);
}
}, [file, startUpload]);
//
useEffect(() => {
return () => {
cleanupUpload();
};
}, [cleanupUpload]);
//
const getStatusText = () => {
switch (uploadStatus) {
case 'idle':
return '等待上传';
case 'checking':
return '检查中...';
case 'initializing':
return '初始化中...';
case 'calculating':
return '计算文件签名中...';
case 'uploading':
return `上传中... (${uploadedChunks}/${totalChunks})`;
case 'merging':
return '合并中...';
case 'completed':
return '上传完成';
case 'instant':
return '秒传成功';
case 'error':
return '上传失败';
case 'cancelled':
return '已取消';
default:
return '未知状态';
}
};
//
const getStatusColor = () => {
switch (uploadStatus) {
case 'completed':
return '#52c41a';
case 'instant':
return '#52c41a';
case 'error':
return '#ff4d4f';
case 'cancelled':
return '#d9d9d9';
default:
return '#1890ff';
}
};
return (
<div className="chunked-upload-wrapper">
<Spin spinning={uploading}>
<div
className={`chunked-upload-area ${uploading ? 'uploading' : ''} ${disabled ? 'disabled' : ''}`}
onDrop={handleDrop}
onDragOver={handleDragOver}
>
{/* 文件选择输入 */}
<input
type="file"
ref={(ref) => {
fileInputRef.current = ref;
}}
style={{ display: 'none' }}
accept={accept}
onChange={handleFileSelect}
disabled={uploading || disabled}
/>
{/* 内容区域 */}
<div className="chunked-upload-content" onClick={() => {
if (!uploading && !disabled && fileInputRef.current) {
fileInputRef.current.click();
}
}}>
{uploadStatus === 'idle' && !uploading ? (
//
children ? (
children
) : (
<div className="chunked-upload-placeholder">
<i className="iconfont icon-shangchuanicon chunked-upload-icon"></i>
<div className="chunked-upload-text">
<span className="chunked-upload-main">点击或拖拽文件到此处</span>
<span className="chunked-upload-hint">支持分片上传最大 {formatFileSize(maxSize)}</span>
</div>
</div>
)
) : (
// /
<div className="chunked-upload-info">
{file && (
<div className="chunked-upload-file-info">
<div className="chunked-upload-file-name">{file.name}</div>
<div className="chunked-upload-file-size">{formatFileSize(file.size)}</div>
</div>
)}
<div className="chunked-upload-progress">
<Progress
percent={progress}
status={uploadStatus === 'error' ? 'exception' : uploadStatus === 'cancelled' ? 'normal' : 'success'}
strokeColor={getStatusColor()}
size="small"
/>
<div className="chunked-upload-status">
<span className="chunked-upload-status-text">{getStatusText()}</span>
{uploadStatus === 'uploading' && uploadSpeed > 0 && (
<span className="chunked-upload-speed">{formatSpeed(uploadSpeed)}</span>
)}
</div>
</div>
{uploadStatus === 'error' && errorMessage && (
<div className="chunked-upload-error">{errorMessage}</div>
)}
</div>
)}
</div>
{/* 操作按钮 */}
{(uploadStatus === 'completed' || uploadStatus === 'instant' || uploadStatus === 'error' || uploadStatus === 'cancelled') && (
<div className="chunked-upload-actions">
{uploadStatus === 'error' && (
<Button
size="small"
onClick={handleRetry}
>
<i className="iconfont icon-refresh mr5"></i>重试
</Button>
)}
{(uploadStatus === 'completed' || uploadStatus === 'instant' || uploadStatus === 'cancelled') && (
<Button
size="small"
danger
onClick={handleCancel}
>
<i className="iconfont icon-delete mr5"></i>移除
</Button>
)}
</div>
)}
</div>
</Spin>
</div>
);
});
ChunkedUpload.displayName = 'ChunkedUpload';
export default ChunkedUpload;

View File

@ -1,181 +0,0 @@
/**
* 分片上传组件样式
*/
.chunked-upload-wrapper {
width: 100%;
.chunked-upload-area {
border: 2px dashed rgba(22, 76, 228, 0.17);
border-radius: 6px;
transition: all 0.3s ease;
cursor: pointer;
text-align: center;
&:hover {
border-color: #164ce4;
.chunked-upload-placeholder {
.chunked-upload-icon {
color: #164ce4;
transform: scale(1.1);
}
}
}
&.uploading {
cursor: default;
border-color: #52c41a;
background-color: #f6ffed;
&:hover {
border-color: #52c41a;
background-color: #f6ffed;
}
}
&.disabled {
cursor: not-allowed;
opacity: 0.6;
&:hover {
border-color: #d9d9d9;
background-color: #fafafa;
}
}
.chunked-upload-content {
padding: 40px 20px;
.chunked-upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.chunked-upload-icon {
font-size: 48px;
color: #8c8c8c;
transition: all 0.3s ease;
margin-bottom: 16px;
}
.chunked-upload-text {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
.chunked-upload-main {
font-size: 16px;
color: #333;
font-weight: 500;
}
.chunked-upload-hint {
font-size: 13px;
color: #8c8c8c;
}
}
}
.chunked-upload-info {
display: flex;
flex-direction: column;
gap: 12px;
.chunked-upload-file-info {
display: flex;
align-items: center;
justify-content: space-between;
.chunked-upload-file-name {
font-size: 14px;
color: #333;
font-weight: 500;
max-width: 300px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chunked-upload-file-size {
font-size: 13px;
color: #8c8c8c;
}
}
.chunked-upload-progress {
.chunked-upload-status {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 8px;
.chunked-upload-status-text {
font-size: 13px;
color: #666;
}
.chunked-upload-speed {
font-size: 12px;
color: #164ce4;
font-weight: 500;
}
}
}
.chunked-upload-error {
padding: 8px 12px;
background-color: #fff2f0;
border: 1px solid #ffccc7;
border-radius: 4px;
font-size: 13px;
color: #ff4d4f;
}
}
}
.chunked-upload-actions {
padding: 12px 20px;
border-top: 1px solid #f0f0f0;
display: flex;
justify-content: flex-end;
gap: 8px;
}
}
// 覆盖 Ant Design Progress 组件样式
:global {
.ant-progress-status-normal {
.ant-progress-bg {
transition: width 0.3s ease;
}
}
.ant-progress-exception {
.ant-progress-text {
color: #ff4d4f !important;
}
}
.ant-progress-success-circle {
.ant-progress-circle-trail {
stroke: #f7f7f7 !important;
}
}
// 按钮样式优化
.ant-btn {
font-size: 13px;
&.ant-btn-dangerous {
&:hover {
background-color: #ff4d4f !important;
border-color: #ff4d4f !important;
color: #fff !important;
}
}
}
}
}

View File

@ -1,19 +0,0 @@
import '../../images/iconfont/iconfont.js';
import { Icon } from 'antd';
const IconFont = Icon.createFromIconfontCN({
scriptUrl: '',
});
/** 封装 iconfont 图标 */
function KFIcon({ type, font = 15, color, className, style, ...rest }) {
const iconStyle = {
...style,
fontSize: font,
color,
};
return <IconFont {...rest} type={type} className={className} style={iconStyle} />;
}
export default KFIcon;

View File

@ -1,20 +0,0 @@
import React from "react";
import { Link } from "react-router-dom";
import arrowIcon from '../../images/icon_arrow.png'
import './index.scss';
//
export const slidingArrowButton = ({href="/", name="探索更多内容", classname="font-16 font-bd", imageSrc, target="_self"}) =>{
return <Link to={href} className={`slidingArrowButton ${classname}`} target={target}>
{name}
<span className="arrowIconBox"><img src={imageSrc || arrowIcon} className="arrowIcon"/></span>
</Link>
}
//
export const rollingButton = ({href="/", name="了解更多", hoverName="了解更多", classname="mt20", target="_self"}) =>{
return <a className={`rollingButton ${classname}`} href={href} target={target}>
<span>{name}</span>
<span>{hoverName}</span>
</a>
}

View File

@ -1,103 +0,0 @@
.slidingArrowButton{
position: relative;
.arrowIconBox{
display: inline-block;
overflow: hidden;
width: 25px;
height: 24px;
position: relative;
top: 5px;
}
.arrowIcon{
width: 36px;
position: absolute;
left: -12px;
top: 7px;
transition: top 0.2s ease-out, left 0.2s ease-out;
}
&.color-white:hover{
color: black !important;
}
&:hover{
.arrowIcon{
left: 7px;
top: -14px;
}
}
}
.rollingButton {
display: inline-block;
color: #ffffff;
font-size: 16px;
background-color: #091221;
border-radius: 18px;
padding: 0 15px;
border: none;
height: 36px;
line-height: 36px;
overflow: hidden;
-webkit-backdrop-filter: blur(24px);
backdrop-filter: blur(24px);
transition: background-color .4s ease-in-out;
position: relative;
&:link, &:visited, &:hover, &:focus{
color: #ffffff;
}
&::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(120deg, rgba(255, 255, 255, 0)0%, rgba(255, 255, 255, 0.5)50%, rgba(255, 255, 255, 0)100%);
transition: left 0.5s ease-in-out;
transition-delay: 0.5s;
}
&.white {
background-color: #ffffff;
color: #091221;
&:hover{
color: #ffffff;
background-color: #091221;
}
}
&.black-border-button{
border:1px solid #091221;
color:#091221;
background-color: transparent;
&:hover{
color:#ffffff;
background-color:#091221;
}
}
&.moreheight-40{
height: 40px;
line-height: 40px;
border-radius: 20px;
&:hover {
span {
transform: translateY(-40px);
}
}
}
span {
display: block;
transition: transform 0.5s;
}
&:hover {
span {
transform: translateY(-36px);
}
&::before {
left: 100%;
}
}
}

View File

@ -1,260 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Button, Popconfirm, Input, message, Spin, Pagination } from 'antd';
import { getImageUrl, timeAgo, isPhone } from 'educoder';
import RenderHtml from '../render-html';
import Attachment from '../../forge/Upload/attachment';
import EditComment from './EditComment';
import CheckProfile from '../../forge/Component/ProfileModal/Profile';
import './comments.scss';
import { Link } from 'react-router-dom';
import { normalizeUrl } from '../../forge/common/util';
import { COMMENT_PAGE_SIZE, COMMENT_CATEGORY } from './constants';
import * as commentApi from './api';
/**
* 通用评论列表组件
* @param {Object} props - 组件属性
* @property {string} type - 评论类型'news' | 'project' | 'resource' | 'skill'
* @property {string|number} id - 资源ID文章ID项目ID等
* @property {Object} [extraParams] - 额外的业务参数
* @property {string} [owner] - 所有者/组织名用于 Markdown 渲染
* @property {string} [projectsId] - 项目 ID用于 Markdown 渲染
* @property {Object} current_user - 当前用户信息
* @property {string} current_user.login - 用户登录名
* @property {boolean} current_user.admin - 是否平台管理员
* @property {string} current_user.image_url - 用户头像URL
* @property {string} current_user.username - 用户姓名
* @property {boolean} [isManager] - 是否为仓库/项目管理员
* @property {Function} [showNotification] - 显示通知
* @property {Function} [showLoginDialog] - 显示登录弹窗
* @property {string} [author] - 评论作者用于权限判断
*/
function CommentsList(props){
const {type, id, extraParams, owner, projectsId, current_user, isManager, showLoginDialog, author, showNotification} = props;
const {login, admin, image_url, username} = current_user;
const [category, setCategory] = useState('comment');
const [journals, setJournals] = useState(undefined);
// /markdown1 2 3 4 5
const [showEdit, setShowEdit] = useState(false);
const [parentId, setParentId] = useState(undefined);
const [replyId, setReplyId] = useState(undefined);
// id
const [updateId, setUpdateId] = useState(undefined);
//
const [open, setOpen] = useState(undefined);
// css 线
const [spin, setSpin] = useState(false);
//
const [journalsCount, setJournalsCount] = useState(undefined);
//
const limit = COMMENT_PAGE_SIZE;
const [page, setPage] = useState(1);
const [totalCount, setTotalCount] = useState(undefined);
const [reload, setReload] = useState(undefined);
// type
useEffect(()=>{
setSpin(true);
let params = {
limit: limit,
page: page
};
commentApi.getCommentList(type, id, page, limit).then(res=>{
if(res && res.data && res.data.data){
const {journals: journalList, total_comment_journals_count, total_count} = res.data.data;
if(category === COMMENT_CATEGORY.ALL){
const array = [];
let start = undefined;
let isJournalCount = 0;
journalList.map((item, index)=>{
!isJournalCount && (start = index);
item.is_journal_detail && (isJournalCount++);
if(!item.is_journal_detail && isJournalCount < 10){
isJournalCount = 0;
return;
}
if(isJournalCount >= 10 && (!item.is_journal_detail || journalList.length-1 === index)){
array.push({start, count: isJournalCount});
isJournalCount = 0;
}
});
array.map((item, i)=>{
journalList[item.start].numCount = item.count-1;
for (let index = 0; index < item.count; index++) {
journalList[item.start+index].start = journalList[item.start].id;
journalList[item.start+index].closeAndSpan = true;
}
});
}
setTotalCount(total_count);
setJournalsCount(total_comment_journals_count);
setJournals(journalList);
setSpin(false);
}
}).catch(err => {
console.error('获取评论列表失败:', err);
setSpin(false);
message.error('获取评论列表失败');
});
}, [type, id, reload, category, page]);
//
const reloadComment = () => {
setReload(Math.random());
};
function commentCtx(v){
return <RenderHtml owner={owner} projectsId={projectsId} className="break_word_comments imageLayerParent commentRenderHtml" value={v} url={{pathname: ''}}/>;
};
//
function deleteCommentFunc(commentId){
commentApi.deleteComment(type, id, commentId).then(res=>{
if(res && res.data && res.data.code === 200){
reloadComment();
message.success('删除成功');
}
}).catch(err => {
console.error('删除评论失败:', err);
message.error('删除评论失败');
});
};
// markdown
function cancelMd(){
setShowEdit(false);
};
// props
const childProps = {
...props,
type,
id,
extraParams,
owner,
projectsId,
current_user,
isManager,
showLoginDialog,
author,
showNotification,
reloadComment,
cancelMd
};
return(
(isPhone() && journalsCount === 0) ? '' : <div className="commentListBox info_text">
{/* 评论快速入口-仅有评论且登录时展示 */}
{login && category !== COMMENT_CATEGORY.OPERATE && journalsCount > 0 && !isPhone() && <div className={`pb30 ${journalsCount > 0 && 'pt15'}`}><div className='gotoComment'>点击<a className='ml5' href='#addComments'>添加评论</a></div></div>}
{/* 评论/操作日志 展示列表 */}
<Spin spinning={spin}>
<div className={`issueCommentsBox ${category === COMMENT_CATEGORY.COMMENT && !spin && 'justComment'}`}>
{journals && (journals.length > 0 && journals.map(item => {return <div key={item.id} className='commentContentBox pb30'>
{/* 评论 */}
<div className='commentOperationBor'></div>
{
item.user.userName ?
<Link to={`/${item.user.userName}`}><img src={normalizeUrl(item.user.avatar)} alt="" className='commentUserImg mr15'/></Link>
:
<span><img src={normalizeUrl(item.user.avatar)} alt="" className='commentUserImg mr15'/></span>
}
<div className='commentContentRight'>
{/* 判断是否是编辑状态 */}
{(showEdit === 2 && updateId === item.id) ? <div className='mt15 mr20'><EditComment extraParams={extraParams} {...childProps} updateId={updateId} content={item.notes} defaultFileList={item.attachments} showUserImg={false}/></div> : <div>
<div className='commentContent'>
<div className='flexCenter font-14'>
<div>
{item.user.userName ? <Link to={`/${item.user.userName}`}>{item.user.nickName}</Link>:<span>{item.user.nickName}</span>}
<span className='ml15 timeAgo font-14'>{timeAgo(item.created_at)}</span>
</div>
{login && !isPhone() && <div>
{/* 平台管理员/仓库管理员/发布评论者/issue创建者 */}
{(admin || isManager || login === item.user.userName || username === author) && <Popconfirm
placement="bottom"
title={`确定要删除此条评论吗?${item.children_journals.length > 0 ? '子评论也将被一起删除。' : ''}`}
okText="是"
cancelText="否"
onConfirm={() => deleteCommentFunc(item.id)}
>
<Button type='link' className='color-grey-89'><i className='iconfont icon-fuzhi-shanchu font-14 mr8'></i>删除</Button>
</Popconfirm>}
{/* 仅评论者可修改 */}
{login === item.user.userName && <Button type='link' className='color-grey-89' onClick={()=>{setUpdateId(item.id); setShowEdit(2)}}><i className='iconfont icon-a-bianji12 font-14 mr8'></i>修改</Button>}
<CheckProfile {...props} sureFunc={()=>{setParentId(item.id); setReplyId(item.id); setShowEdit(3)}}><Button type='link' className='color-grey-89'><i className='iconfont icon-a-xiaoxi1 font-14 mr8'></i>回复</Button></CheckProfile>
</div>}
</div>
<div className='contentHtml mb5'>{commentCtx(item.notes)}</div>
{item && item.attachments && item.attachments.length > 0 && <div className='attachmentBox mb5'><Attachment
attachments={item.attachments}
showNotification={showNotification}
canDelete={false}
/></div>}
</div>
</div>}
{showEdit === 3 && replyId === item.id && <div className='contentHtml mr20'><EditComment extraParams={extraParams} {...childProps} parentId={parentId} replyId={replyId}/></div>}
{/* 评论回复部分 */}
{item.children_journals.map(i =>{
return <div className='commentReply' key={i.id}>
{(showEdit === 4 && updateId === i.id) ? <div className='mr20'><EditComment extraParams={extraParams} {...childProps} updateId={updateId} content={i.notes} defaultFileList={i.attachments}/></div> : <div>
<div className='flexCenter'>
<div>
<Link to={`/${i.user.userName}`}><img src={normalizeUrl(i.user.avatar)} alt="" className='commentUserImg mr8'/></Link>
<Link to={`/${i.user.userName}`}>{i.user.nickName}</Link>
{i.reply_user && <span className='ml5 timeAgo mr3'>回复</span>}
<span>{i.reply_user && i.reply_user.name}</span>
<span className='ml15 timeAgo font-14'>{timeAgo(i.created_at)}</span>
</div>
{login && !isPhone() && <div>
{/* 平台管理员/仓库管理员/发布评论者/回复评论者/issue创建者 */}
{(admin || isManager || login === i.user.userName || (i.reply_user && login === i.reply_user.login) || username === author) && <Popconfirm
placement="bottom"
title={"确定要删除当前回复吗?"}
okText="是"
cancelText="否"
onConfirm={() => deleteCommentFunc(i.id)}
>
<Button type='link' className='color-grey-89'><i className='iconfont icon-fuzhi-shanchu font-14 mr8'></i>删除</Button>
</Popconfirm>}
{/* 仅回复评论者可修改 */}
{login === i.user.userName && <Button type='link' className='color-grey-89' onClick={()=>{setUpdateId(i.id); setShowEdit(4)}}><i className='iconfont icon-a-bianji12 font-14 mr8'></i>修改</Button>}
<CheckProfile {...props} sureFunc={()=>{setParentId(item.id);setReplyId(i.id); setShowEdit(5)}}>
<Button type='link' className='color-grey-89'><i className='iconfont icon-a-xiaoxi1 font-14 mr8'></i>回复</Button>
</CheckProfile>
</div>}
</div>
<div className='contentHtml mt5 mb10'>{commentCtx(i.notes)}</div>
{i && i.attachments && i.attachments.length > 0 && <div className='attachmentBox'><Attachment
attachments={i.attachments}
showNotification={showNotification}
canDelete={false}
/></div>}
</div>}
{showEdit === 5 && replyId === i.id && <div className='contentHtml mr20'><EditComment extraParams={extraParams} {...childProps} parentId={parentId} replyId={replyId}/></div>}
</div>})}
</div>
</div>}))}
</div>
</Spin>
{totalCount > limit && <div className='mt20 paginationIssueComment mb20'>
<Pagination simple current={page} pageSize={limit} total={totalCount} onChange={(page)=>setPage(page)}/>
</div>}
{/* 添加评论 */}
{category !== COMMENT_CATEGORY.OPERATE && !isPhone() && <div className="pb30">
{login ? showEdit === 1 ? <EditComment extraParams={extraParams} {...childProps}/> : <div className="addComments" id='addComments'>
<img src={getImageUrl(image_url)} alt="" />
<div style={{flex:1}}>
<CheckProfile {...props} sureFunc={()=>{setShowEdit(1);}}>
<Input className='addCommentBox' placeholder="添加评论"/>
</CheckProfile>
</div>
</div> : <div className='unLoginComment font-15 pl20'>
{/* 未登录用户 */}
<a className='mr5 loginBtn' onClick={()=>{showLoginDialog()}}>登录</a>并参与评论与回复
</div>}
</div>}
</div>
);
};
export default CommentsList;

View File

@ -1,185 +0,0 @@
import React, { useState } from 'react';
import { Button, Icon, message, Upload } from 'antd';
import { getImageUrl, getZoneUrl } from 'educoder';
import MDEditor from "../../modules/tpm/challengesnew/tpm-md-editor";
import Attachments from "../../forge/Upload/attachment";
import './comments.scss';
import { Link } from 'react-router-dom';
import * as commentApi from './api';
import cookie from 'react-cookies';
/**
* 通用评论编辑组件
* @param {Object} props - 组件属性
* @property {string} type - 评论类型'news' | 'project' | 'resource' | 'skill'
* @property {string|number} id - 资源ID
* @property {Object} [extraParams] - 额外的业务参数
* @property {string} [owner] - 所有者/组织名用于 Markdown 编辑器 @ 功能
* @property {string} [projectsId] - 项目 ID用于 Markdown 编辑器 @ 功能
* @property {Object} current_user - 当前用户信息
* @property {string} current_user.login - 用户登录名
* @property {string} current_user.image_url - 用户头像URL
* @property {Function} cancelMd - 取消编辑回调
* @property {Function} reloadComment - 刷新评论列表回调
* @property {Function} showNotification - 显示通知
* @property {string} [content] - 编辑时的原始内容
* @property {Array} [defaultFileList] - 默认附件列表
* @property {string} [parentId] - 父评论ID
* @property {string} [replyId] - 回复的评论ID
* @property {string} [updateId] - 正在编辑的评论ID
* @property {boolean} [showUserImg=true] - 是否显示用户头像
*/
function EditComment(props) {
const { type, id, extraParams, current_user, current_user: { login, image_url }, owner, projectsId, cancelMd, reloadComment, showNotification, parentId, replyId, updateId, showUserImg = true, content: propContent, defaultFileList: propDefaultFileList } = props;
const [content, setContent] = useState(propContent);
const [quillFlag, setQuillFlag] = useState(false);
// loading
const [journalSpin, setJournalSpin] = useState(false);
const [fileList, setFileList] = useState(propDefaultFileList || []);
//
function addJournals() {
if (!content) {
setQuillFlag(true);
return;
}
setJournalSpin(true);
const fileIds = fileList.map(file=>file.fileIdentifier || file.identifier).toString()
if (updateId) {
//
const params = {
id: updateId,
notes: content,
attachment_identifiers: fileIds,
};
commentApi.editComment(type, id, params).then(res => {
if (res && res.data && res.data.code === 200) {
//
reloadComment();
message.success('编辑成功');
cancelMd();
setContent(undefined);
}
setJournalSpin(false);
}).catch(err => {
console.error('编辑评论失败:', err);
message.error('编辑评论失败');
setJournalSpin(false);
});
} else {
//
const params = {
parent_id: parentId,
reply_id: replyId,
notes: content,
attachment_identifiers: fileIds,
};
commentApi.addComment(type, id, params).then(res => {
if (res && res.data && res.data.code === 200) {
//
reloadComment();
message.success('评论成功');
cancelMd();
setContent(undefined);
}
setJournalSpin(false);
}).catch(err => {
console.error('添加评论失败:', err);
message.error('添加评论失败');
setJournalSpin(false);
});
}
};
function beforeUpload(file) {
const isLt100M = file.size / 1024 / 1024 < 100;
if (!isLt100M) {
message.error(`文件大小必须小于${100}MB!`);
}
return isLt100M;
}
function changeFileList(info) {
const { file } = info;
const { status, response } = file || {}
if (status === 'done' || status === 'removed') {
setFileList([
...fileList,
response
]);
}
}
return (
<div className="grid-item-top pb10">
<Link
to={`/${current_user && current_user.login}`}
className="show-user-link mr10"
>
<img
className="radius"
src={getImageUrl(`/${current_user && current_user.image_url}`)}
alt=""
width="30"
height="30"
style={{ display: showUserImg ? '' : 'none' }}
/>
</Link>
<div style={{ position: "relative" }}>
<MDEditor
placeholder={"添加评论..."}
height={300}
mdID={"orderdetail-add-descriptions" + replyId}
initValue={content}
onChange={(value) => { setQuillFlag(false); setContent(value); }}
owner={owner}
projectsId={projectsId}
></MDEditor>
<p className="quillFlagBox">
{quillFlag && <span>请输入评论内容</span>}
</p>
<Upload.Dragger
showUploadList={false}
data={commentApi.uploadCommentFileData(type)}
action={getZoneUrl(`/api/file/common/upload`)}
beforeUpload={beforeUpload}
onChange={changeFileList}
withCredentials={true}
headers={{
Authorization: cookie.load('autologin_trustie'),
}}
>
<i className='iconfont icon-shangchuan font-30'></i>
<p className="mt10">点击或拖拽文件到此区域上传文件大小不超过 100MB</p>
</Upload.Dragger>
{fileList && fileList.length > 0 && <div className='mt10'>
{fileList.map(file => {
return <div className='df-center'>
<i className="iconfont icon-fujian mr8 paper-clip-color font-14"></i>
<span className='font-16'>{file.fileOriginName || file.title}</span>
<span className='ml20 mrat'>{file.fileSize || file.filesize}</span>
<a onClick={() => {
const deletedList = fileList.filter(i => i.fileId !== file.fileId || i.id !== file.id);
setFileList(deletedList);
}}><i className='iconfont icon-shanchuicon3'></i></a>
</div>
})}
</div>}
<p className="clearfix mt20">
<Button
type="primary"
onClick={addJournals}
loading={journalSpin}
className="mr15 colorButton"
>
评论
</Button>
<Button className="colorButton2" onClick={() => { cancelMd() }}>取消</Button>
</p>
</div>
</div>
);
}
export default EditComment;

View File

@ -1,114 +0,0 @@
/**
* 通用评论 API 调用器
* 根据 type 参数调用不同的业务接口
*/
import { COMMENT_TYPE } from './constants';
import {
getJournalList,
addJournal,
editJournal,
deleteJournal,
uploadFileBySourceJournal
} from '../../forge/Information/api';
/**
* 获取评论列表
* @param {string} type - 评论类型
* @param {string|number} id - 资源 ID
* @param {Object} params - 分页参数
* @returns {Promise}
*/
export async function getCommentList(type, id, page, limit) {
switch (type) {
case COMMENT_TYPE.RESOURCE:
return await getJournalList({
journalized_id: id,
journalized_type: type,
pageNum: page,
pageSize: limit
})
default:
throw new Error(`Unsupported comment type: ${type}`);
}
}
/**
* 新增评论
* @param {string} type - 评论类型
* @param {string|number} id - 资源 ID
* @param {Object} data - 评论数据
* @returns {Promise}
*/
export async function addComment(type, id, data) {
switch (type) {
case COMMENT_TYPE.RESOURCE:
return await addJournal({
journalized_id: id,
journalized_type: type,
...data
})
default:
throw new Error(`Unsupported comment type: ${type}`);
}
}
/**
* 编辑评论
* @param {string} type - 评论类型
* @param {string|number} id - 资源 ID
* @param {Object} data - 评论数据包含 id 字段
* @returns {Promise}
*/
export async function editComment(type, id, data) {
switch (type) {
case COMMENT_TYPE.RESOURCE:
return await editJournal({
id: id,
...data
})
default:
throw new Error(`Unsupported comment type: ${type}`);
}
}
/**
* 删除评论
* @param {string} type - 评论类型
* @param {string|number} id - 资源 ID
* @param {string|number} commentId - 评论 ID
* @returns {Promise}
*/
export async function deleteComment(type, id, commentId) {
switch (type) {
case COMMENT_TYPE.RESOURCE:
return await deleteJournal(commentId)
default:
throw new Error(`Unsupported comment type: ${type}`);
}
}
/**
* @获取文件上传路径
* pms-gitlink会传到ruby的文件服务
* contentType: 如果文件上传路径为/api/file/common/upload需要指定内容属于哪个模块egproducts/projects/docs/ docs/{projectId}
*/
export async function uploadCommentFile(type, data) {
switch (type) {
case COMMENT_TYPE.RESOURCE:
return await uploadFileBySourceJournal(data)
default:
break;
}
}
export function uploadCommentFileData(type) {
switch (type) {
case COMMENT_TYPE.RESOURCE:
return {
hierarchy: "Journals",
type: "ai-resource"
}
default:
break;
}
}

View File

@ -1,205 +0,0 @@
.typeActionBox{
display: flex;
align-items: center;
font-size: 16px;
.line {
width: 3px;
height: 14px;
background: var(--primary-color);
margin-right: 13px;
}
}
.commentUserImg{
height: 28px;
width: 28px;
border-radius: 50%;
object-fit: cover;
}
.issueCommentsBox{
.operationLog, .commentContentBox{
position: relative;
min-height: 62px;
display: flex;
>.flexCenter, >a{
z-index: 2;
}
&::before, &::after{
content: '';
width: 1px;
height: 50%;
position: absolute;
background: #eee;
left: 12px;
z-index: 1;
}
&::after{
background: #eee;
top: 50%;
}
}
.commentContentBox+.operationLog .operationCommentBor, .operationLog+.commentContentBox .commentOperationBor{
border-top: 1px solid #eee;
width: 98.5%;
position: absolute;
left: 12px;
}
.operationLog+.commentContentBox{
&>a, &>.commentContentRight{
margin-top: 25px;
}
}
.timeAgo{
color:#acb0bf;
}
.operationLog:last-child::after, .operationLog:first-child::before, .commentContentBox:last-child::after, &.justComment .commentContentBox::before, &.justComment .commentContentBox::after{
display: none;
}
.operationLog:first-child, .commentContentBox+.operationLog{
margin-top: -15px;
}
.commentContentBox:last-child::before{
height: 35%;
}
.flexCenter{
display: flex;
align-items: center;
justify-content: space-between;
}
.commentRenderHtml.markdown-body p{
font-size: 14px !important;
}
.commentReply{
padding: 15px 0 0 20px;
background-color: hsla(225, 31%, 95%, 0.41);
&>div{
padding-bottom: 5px;
}
&+.commentReply>div{
margin-top: -15px;
padding-top: 15px;
border-top: 1px dashed #eee;
}
}
.opBox{
justify-content: flex-start;
}
.commentContentBox{
display: flex;
align-items: flex-start;
}
.commentContentRight{
flex: 1;
background-color:#fafafc;
border:1px solid rgba(42, 97, 255, 0.23);
border-radius:6px;
position: relative;
top: -6px;
padding: 10px 15px 16px;
width: 0;
&::before{
content: '';
display: block;
position: absolute;
top: 13px;
left: -14px;
width: 0;
height: 0;
overflow: hidden;
font-size: 0;
line-height: 0;
border: 7px;
border-style: solid;
border-color: transparent rgba(42, 97, 255, 0.23) transparent transparent;
}
&::after{
content: '';
display: block;
width: 0;
height: 0;
overflow: hidden;
font-size: 0;
line-height: 0;
border: 6px;
border-style: solid;
border-color: transparent #fafafc transparent transparent;
position: absolute;
top: 14px;
left: -11px;
}
}
}
.attachmentBox{
margin-top: -8px;
}
.color-grey-89{
color: #898d9d;
}
// 添加评论样式
.unLoginComment{
height:58px;
line-height: 58px;
background-color:var(--more-light-color);
border-radius:4px;
color: rgba(84, 87, 103, 1);
.loginBtn{
color: var(--primary-color);
}
}
.quillFlagBox{
color: red;
margin-bottom: 5px !important;
height: 28px;
}
.addComments{
display: flex;
align-items: center;
background-color:var(--more-light-color);
border-radius:4px;
padding:7px 10px;
&>img{
height: 28px;
width: 28px;
border-radius: 50%;
margin-right: 13px;
}
}
.addCommentBox{
width: 100%;
height: 30px;
border: none;
&:focus {
border-color: var(--primary-color) !important;
}
}
.paginationIssueComment{
text-align: center;
}
.gotoComment{
background-color:rgba(241, 243, 252, 0.55);
border-radius:4px;
color: rgba(172, 176, 191, 1);
padding: 8px 20px;
margin-top: -10px;
&>a{color: var(--primary-color);}
}
.colorButton {
background-color: var(--primary-color);
border-color: var(--primary-color);
&:hover {
background-color: var(--light-color);
border-color: var(--primary-color);
}
&:focus {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
}
.colorButton2 {
&:hover {
border-color: var(--primary-color);
color: var(--light-color);
}
&:focus {
color: var(--primary-color);
}
}

View File

@ -1,45 +0,0 @@
/**
* 评论组件类型枚举
*/
export const COMMENT_TYPE = {
NEWS: 'news', // 资讯/文章评论
PROJECT: 'project', // 项目评论
RESOURCE: 'resource', // 资源评论
SKILL: 'skill' // AI 技能评论
};
/**
* 评论组件 Props 类型定义JSDoc 用于 TypeScript/IDE 提示
* @typedef {Object} CommentsListProps
* @property {string} type - 评论类型'news' | 'project' | 'resource' | 'skill'
* @property {string|number} id - 资源 ID文章 ID项目 ID资源 ID
* @property {Object} [extraParams] - 额外的业务参数根据不同 type 传递不同参数
* @property {string} [owner] - 所有者/组织名用于 Markdown 渲染
* @property {string} [projectsId] - 项目 ID用于 Markdown 渲染
* @property {Object} current_user - 当前用户信息
* @property {string} current_user.login - 用户登录名
* @property {boolean} current_user.admin - 是否管理员
* @property {string} current_user.image_url - 用户头像 URL
* @property {string} current_user.username - 用户姓名
* @property {boolean} [isManager] - 是否为仓库/项目管理员
* @property {string} [author] - 评论作者用于权限判断
* @property {Function} [reload] - 触发重新加载
* @property {Function} [showNotification] - 显示通知
* @property {Function} [showLoginDialog] - 显示登录弹窗
* @property {Function} [onCommentAdded] - 评论添加回调
* @property {Function} [onCommentDeleted] - 评论删除回调
*/
/**
* 评论列表分页大小
*/
export const COMMENT_PAGE_SIZE = 50;
/**
* 评论分类
*/
export const COMMENT_CATEGORY = {
COMMENT: 'comment', // 仅评论
ALL: 'all', // 评论 + 操作日志
OPERATE: 'operate' // 仅操作日志
};

View File

@ -1,62 +0,0 @@
/**
* 通用评论组件入口
*
* 基于 src/forge/Information/Component/comments 组件重构
* 支持通过 type 参数区分不同的业务场景
*
* 使用示例
*
* import CommentsList, { COMMENT_TYPE } from '@/components/comments';
*
* <CommentsList
* type={COMMENT_TYPE.NEWS}
* id={docId}
* owner={owner}
* projectsId={projectsId}
* current_user={currentUser}
* isManager={isManager}
* author={author}
* showNotification={showNotification}
* showLoginDialog={showLoginDialog}
* />
*
* 不同业务模块传参示例
*
* // News
* <CommentsList
* type={COMMENT_TYPE.NEWS}
* id={articleId}
* owner={owner}
* projectsId={projectsId}
* current_user={currentUser}
* author={articleAuthor}
* ...
* />
*
* // Project API
* <CommentsList
* type={COMMENT_TYPE.PROJECT}
* id={projectId}
* owner={owner}
* projectsId={projectId}
* current_user={currentUser}
* author={projectCreator}
* ...
* />
*/
import CommentsList from './CommentsList';
import EditComment from './EditComment';
import { COMMENT_TYPE, COMMENT_PAGE_SIZE, COMMENT_CATEGORY } from './constants';
import * as commentApi from './api';
export {
CommentsList,
EditComment,
COMMENT_TYPE,
COMMENT_PAGE_SIZE,
COMMENT_CATEGORY,
commentApi
};
export default CommentsList;

View File

@ -135,14 +135,6 @@ export default ({
}
}
// - 使
const handleImageError = (e) => {
const img = e.target;
//
if (img.src === imgError) return;
img.src = imgError;
};
useEffect(() => {
if (el.current && html) {
if (html.match(preRegex)) {
@ -151,11 +143,8 @@ export default ({
}
if (el.current) {
el.current.addEventListener('click', onAncherHandler)
// 使
el.current.addEventListener('error', handleImageError, true);
return () => {
el.current.removeEventListener('click', onAncherHandler);
el.current.removeEventListener('error', handleImageError, true);
el.current.removeEventListener('click', onAncherHandler)
resetMathExpressions()
cleanToc()
}

View File

@ -1,9 +0,0 @@
import React from "react";
import './index.scss';
// ,
export const HoverUnderline = ({classname="", children}) =>{
return <div className={`hover-underline ${classname}`}>
{children}
</div>
}

View File

@ -1,43 +0,0 @@
.hover-underline {
position: relative;
&::after {
position: absolute;
bottom: 0;
left: 0;
content: '';
width: 0;
height: 1px;
background-color: #2149d4;
transition: all 0.5s ease;
}
&:hover {
padding-right: 25px;
&+.hot-tag {
display: none;
}
}
&:hover::after {
width: 100%;
}
&:hover::before {
content: "\e9c4";
font-family: 'iconfont';
font-weight: normal;
position: absolute;
right: 0;
font-size: 15px;
top: 3px;
}
&.font-22:hover::before{
font-size: 17px;
top: 4px;
}
&.font-20:hover::before{
top: 5px;
}
}

View File

@ -1,16 +0,0 @@
export const factoryZoneId = 102
export const dirId = 9
export const versionId = '工厂版本'
export const lessonId = '学习资源'
export const commontoolId = '共建工具'
// export const commontoolId = '开发工具'
export const toolId = '开发工具'
export const standardId = '标准规范'
export const docId = '文档资料'
export const manualId = '用户手册'
export const sceneId = '应用场景'
export const factoryZoneName = 'tester'

View File

@ -1,216 +0,0 @@
import fetch, { httpUrl } from '../forge/Information/fetch';
export { httpUrl }
export function getNewsHotList(id,params){
return fetch({
url:`/cms/doc/open/zone/${id}/hotDocList`,
method: 'get',
params
})
}
export function getDocList(id, params){
return fetch({
url:`/cms/doc/open/zone/${id}/docOverviewList`,
method: 'get',
params
})
}
// 获取文章列表
export function getSubDocList(id,params){
return fetch({
url:`/cms/doc/open/dir/${id}/docList`,
method: 'get',
params
})
}
export function getProjectsLists(id,params){
return fetch({
url:`/zone/open/${id}/project/list`,
method: 'get',
params
})
}
/**
* 获取资源列表
* @param {*} params
* @returns
*/
export async function getSourceList(params){
const res = await fetch({
url:`/zone/open/${params.id}/resource/list`,
method: 'get',
params
});
if (res && res.data && res.data.rows) {
res.data.rows = res.data.rows.map(row => {
if (row && row.extendData) {
return { ...row, extendData: JSON.stringify(row.extendData) };
}
return row;
});
}
return res;
}
/**
* 获取资源领域列表
* @param {*} id
* @returns
*/
export function getSourceZoneList(id){
return fetch({
url:`/zone/open/${id}/resourceDomain/list`,
method: 'get',
})
}
export function gethomePageDocList(id) {
return fetch({
url: `/cms/doc/open/zone/${id}/homePageDocList`,
method: 'get',
});
}
export function getNewsDetail(id){
return fetch({
url:`/cms/doc/open/${id}`,
method: 'get',
})
}
export async function getSourceDetail(id){
const res = await fetch({
url:`/zone/open/resource/detail/${id}`,
method: 'get'
});
if (res && res.data && res.data.data && res.data.data.extendData) {
res.data.data.extendData = JSON.stringify(res.data.data.extendData);
}
return res;
}
export function getMainInfos(deptId, params) {
return fetch({
url: `/zone/open/zoneKey/${deptId}`,
method: 'get',
params
});
}
// ==================== 分片上传相关 API ====================
/**
* 初始化分片上传
* @param {Object} data - 初始化请求参数
* @param {string} data.fileName - 文件名
* @param {number} data.fileSize - 文件大小字节
* @param {string} [data.type] - 文件类型cms/dms/resource/pms等
* @param {string} [data.hierarchy] - 层级结构
* @returns {Promise}
*/
export function initChunkedUpload(data) {
return fetch({
url: '/file/chunked/init',
method: 'post',
data
});
}
/**
* 上传分片
* @param {Object} data - 分片上传参数
* @param {string} data.uploadId - 上传ID
* @param {number} data.chunkNumber - 分片序号
* @param {File} data.chunk - 分片文件
* @param {Function} [onUploadProgress] - 上传进度回调
* @returns {Promise}
*/
export function uploadChunk(data, onUploadProgress) {
// 使用 FormData 包装数据,让浏览器自动设置 boundary
const formData = new FormData();
formData.append('chunk', data.chunk);
formData.append('uploadId', data.uploadId);
formData.append('chunkNumber', data.chunkNumber);
return fetch({
url: `/file/chunked/upload/${data.uploadId}/${data.chunkNumber}`,
method: 'post',
data: formData,
timeout: 600000,
onUploadProgress
});
}
/**
* 合并分片
* @param {string} uploadId - 上传ID
* @returns {Promise}
*/
export function mergeChunks(uploadId) {
return fetch({
url: `/file/chunked/merge/${uploadId}`,
method: 'post'
});
}
/**
* 取消上传
* @param {string} uploadId - 上传ID
* @returns {Promise}
*/
export function cancelUpload(uploadId) {
return fetch({
url: `/file/chunked/cancel/${uploadId}`,
method: 'delete'
});
}
/**
* 检查分片是否已上传
* @param {string} uploadId - 上传ID
* @param {number} chunkNumber - 分片序号
* @returns {Promise}
*/
export function checkChunk(uploadId, chunkNumber) {
return fetch({
url: `/file/chunked/check/${uploadId}/${chunkNumber}`,
method: 'get'
});
}
/**
* 获取上传进度
* @param {string} uploadId - 上传ID
* @returns {Promise}
*/
export function getProgress(uploadId) {
return fetch({
url: `/file/chunked/progress/${uploadId}`,
method: 'get'
});
}
/**
* 检查秒传/断点续传
* @param {Object} data - 检查参数
* @param {string} data.uploadId - 上传ID
* @param {string} data.fileSignature - 文件签名
* @returns {Promise}
*/
export function checkInstantUpload(data) {
return fetch({
url: '/file/chunked/checkInstantUpload',
method: 'post',
data
});
}

View File

@ -1,61 +0,0 @@
.shiny-button {
overflow: hidden;
position: relative;
border: 2px solid transparent;
background-clip: padding-box, border-box;
background-image: linear-gradient(to bottom right, #0b0333, #091221), linear-gradient(125deg, hsla(0, 0%, 100%, 0) 45%, hsla(0, 0%, 100%, .6) 50%, hsla(0, 0%, 100%, 0) 53%);
background-origin: border-box;
background-position: 0, -50%;
background-size: 100%, 200%;
&::after {
animation: glint 4s linear infinite backwards;
animation-delay: -3s;
background-image: linear-gradient(125deg, hsla(0, 0%, 100%, 0) 30%, hsla(0, 0%, 100%, .4) 50%, hsla(0, 0%, 100%, 0) 52%);
content: " ";
height: 200%;
left: -120%;
position: absolute;
top: -50%;
width: 200%;
}
&:hover {
animation: border-glint 2s linear;
border: 2px solid transparent !important;
background-image: linear-gradient(to bottom right, #0b0333, #091221), linear-gradient(125deg, hsla(0, 0%, 100%, 0) 45%, hsla(0, 0%, 100%, .6) 50%, hsla(0, 0%, 100%, 0) 53%) !important;
}
}
@keyframes border-glint {
0% {
background-position: 0, 115%;
}
25% {
background-position: 0, 75%;
}
75% {
background-position: 0, -15%;
}
100% {
background-position: 0, -15%;
}
}
@keyframes glint {
0% {
left: -120%;
}
8% {
left: 50%;
}
100% {
left: 50%;
}
}

View File

@ -1,77 +0,0 @@
import { useState, useEffect, useRef } from 'react';
import './index.scss';
const AutoSwitchText = ({
texts = [],
interval = 2000,
autoPlay = true,
transitionDuration = 2000, //
}) => {
const [activeIndex, setActiveIndex] = useState(0);
const [isTransitioning, setIsTransitioning] = useState(false);
const timerRef = useRef(null);
const containerRef = useRef(null);
//
const switchToNext = () => {
if (texts.length <= 1 || isTransitioning) return;
setIsTransitioning(true);
// 1
if (containerRef.current) {
containerRef.current.style.transform = 'scaleX(0.66)';
containerRef.current.style.filter = 'blur(3px)';
containerRef.current.style.opacity = '0.4';
}
//
setTimeout(() => {
//
setActiveIndex((prev) => (prev + 1) % texts.length);
// 2
setTimeout(() => {
if (containerRef.current) {
containerRef.current.style.transform = 'scaleX(1)';
containerRef.current.style.filter = 'blur(0)';
containerRef.current.style.opacity = '0.64';
}
//
setTimeout(() => {
setIsTransitioning(false);
}, transitionDuration * 0.3);
}, transitionDuration * 0.2);
}, transitionDuration * 0.5);
};
//
useEffect(() => {
if (!autoPlay || texts.length <= 1) return;
const startInterval = () => {
timerRef.current = setInterval(switchToNext, interval);
};
startInterval();
return () => {
if (timerRef.current) {
clearInterval(timerRef.current);
}
};
}, [texts, interval, autoPlay]);
if (texts.length === 0) return null;
return (
<div
ref={containerRef}
className="move-item"
>
{texts[activeIndex]}
</div>
);
};
export default AutoSwitchText;

View File

@ -1,21 +0,0 @@
.move-item {
position: absolute;
top: 50px;
left: 400px;
opacity: 64%;
height: 24px;
background: #ffffff;
border-radius: 22px;
max-width: 114px;
min-width: 80px;
line-height: 24px;
font-family: alibabareg;
color: #37393d;
font-size: 11px;
text-align: center;
padding: 0px 10px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
transition: all 1s ease-in-out;
}

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