You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.vue 21 kB

2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. <template>
  2. <div>
  3. <TopHeader :menu="''"></TopHeader>
  4. <div class="ui container">
  5. <div class="title">您申请的项目将在2030科技项目页面展示</div>
  6. <div class="form-c" v-show="!selectTechPrj">
  7. <div class="form-wrap">
  8. <div class="form-header">申请展示项目</div>
  9. <div class="form-content">
  10. <div class="form-row">
  11. <div class="row-label required">申请项目存放于</div>
  12. <div class="row-content">
  13. <el-radio v-model="form.type" label="openi" @input="changeType()">启智社区</el-radio>
  14. <el-radio v-model="form.type" label="no-openi" @input="changeType()">非启智社区</el-radio>
  15. </div>
  16. </div>
  17. <div class="form-row" :class="form.url_err ? 'form-row-err' : ''">
  18. <div class="row-label required">现项目地址</div>
  19. <div class="row-content">
  20. <el-input size="medium" v-model="form.url" @input="changeUrl" class="can-err"
  21. placeholder='请输入现有项目的 HTTP(s) 或 Git " clone" URL,如:https://openi.pcl.ac.cn/OpenI/aiforge'></el-input>
  22. </div>
  23. </div>
  24. <div v-show="form.type == 'no-openi'">
  25. <div class="form-row" :class="form.alias_err ? 'form-row-err' : ''">
  26. <div class="row-label required baseline">启智项目名称</div>
  27. <div class="row-content">
  28. <div>
  29. <el-input size="medium" v-model="form.repo_alias" @input="changeAlias" class="can-err"></el-input>
  30. </div>
  31. <div class="tips">
  32. 请输入中文、字母、数字和-_ .,最多100个字符。
  33. </div>
  34. </div>
  35. </div>
  36. <div class="form-row" :class="form.name_err ? 'form-row-err' : ''">
  37. <div class="row-label required baseline">启智项目路径</div>
  38. <div class="row-content">
  39. <div class="reop-url-c">
  40. <el-select size="medium" class="owner-sel" v-model="form.uid" @change="changeOwner">
  41. <div slot="prefix" class="owner-item" style="height:100%;padding-left:10px">
  42. <img class="owner-img" :src="ownerSelect.RelAvatarLink">
  43. </div>
  44. <el-option v-for="item in ownerList" :key="item.value" :value="item.value" :label="item.label">
  45. <div class="owner-item">
  46. <img class="owner-img" :src="item.RelAvatarLink">
  47. <span class="owner-name">{{(item.FullName || item.Name)}}</span>
  48. </div>
  49. </el-option>
  50. </el-select>
  51. <span style="margin: 0 8px;font-size:22px;"> / </span>
  52. <el-input size="medium" v-model="form.repo_name" @input="changeRepoName" class="can-err"></el-input>
  53. </div>
  54. <div class="tips">
  55. 启智项目地址:<span class="openi-repo-url">{{ form.repo_url }}</span>
  56. </div>
  57. </div>
  58. </div>
  59. <div class="form-row" :class="form.topic_err ? 'form-row-err' : ''">
  60. <div class="row-label required">关键词</div>
  61. <div class="row-content">
  62. <el-select style="width:100%" size="medium" v-model="form.topics" multiple filterable remote
  63. allow-create default-first-option placeholder="请选择标签" :remote-method="searchTopics"
  64. :loading="form.topicLoading" class="can-err">
  65. <el-option v-for="item in topicsList" :key="item.value" :label="item.label" :value="item.value">
  66. </el-option>
  67. </el-select>
  68. </div>
  69. </div>
  70. <div class="form-row" :class="form.descr_err ? 'form-row-err' : ''">
  71. <div class="row-label required baseline">项目简介</div>
  72. <div class="row-content">
  73. <el-input size="medium" type="textarea" :rows="4" placeholder="请输入项目简介(长度不超过255)" :maxlength="255"
  74. show-word-limit v-model="form.description" class="can-err"></el-input>
  75. </div>
  76. </div>
  77. </div>
  78. <div class="form-row" :class="form.tech_err ? 'form-row-err' : ''">
  79. <div class="row-label required">科技项目</div>
  80. <div class="row-content">
  81. <el-input placeholder="请选择科技项目" size="medium" v-model="form.tech_show" :readonly="true" class="can-err">
  82. <el-button size="medium" slot="append" class="btn-select" @click="goSelectTechPrj">选择科技项目</el-button>
  83. <el-button size="medium" slot="append" class="btn-clear" @click="clearTechPrj">清除</el-button>
  84. </el-input>
  85. </div>
  86. </div>
  87. <div class="form-row" :class="form.institution_err ? 'form-row-err' : ''">
  88. <div class="row-label required">成果贡献单位</div>
  89. <div class="row-content">
  90. <el-select style="width:100%" size="medium" v-model="form.institution" multiple class="can-err">
  91. <el-option v-for="item in institutionList" :key="item.key" :label="item.value"
  92. :value="item.value"></el-option>
  93. </el-select>
  94. </div>
  95. </div>
  96. <div class="form-btn-group">
  97. <el-button size="medium" type="primary" :loading="submitLoading" class="btn confirm-btn"
  98. @click="submit">提交申请</el-button>
  99. <el-button size="medium" class="btn" @click="cancel">{{ $t('cancel') }}</el-button>
  100. </div>
  101. </div>
  102. </div>
  103. </div>
  104. <div class="form-select-tech-prj" v-show="selectTechPrj">
  105. <div class="form-wrap">
  106. <div class="form-header">
  107. <span>选择科技项目</span>
  108. <span>
  109. <el-button class="form-btn-go-back" @click="goBack">返回</el-button>
  110. </span>
  111. </div>
  112. <div class="form-content">
  113. <div class="form-row">
  114. <div class="row-label">请输入科技项目</div>
  115. <div class="row-content">
  116. <el-input placeholder="请输入搜索内容" size="medium" v-model="form.tech_search_keyword"
  117. @keyup.enter.native="searchTechList" class="input-with-select">
  118. <el-select v-model="form.tech_search_sel" style="width:142px" size="medium" slot="prepend">
  119. <el-option label="项目立项编号" value="0"></el-option>
  120. <el-option label="参与单位" value="1"></el-option>
  121. <el-option label="项目名称" value="2"></el-option>
  122. </el-select>
  123. <el-button size="medium" slot="append" icon="el-icon-search" @click="searchTechList"></el-button>
  124. </el-input>
  125. </div>
  126. </div>
  127. <div class="form-table">
  128. <div style="margin: 0 20px 30px 20px">
  129. <el-table ref="tableRef" border :data="tableData" style="width:100%" v-loading="loading" stripe>
  130. <el-table-column prop="no" label="项目立项编号" align="center" header-align="center"
  131. width="120"></el-table-column>
  132. <el-table-column prop="name" label="科技项目名称" align="center" header-align="center"
  133. width="200"></el-table-column>
  134. <el-table-column prop="institution" label="项目承担单位" align="center" header-align="center"
  135. width="200"></el-table-column>
  136. <el-table-column prop="all_institution" label="所有参与单位" align="center"
  137. header-align="center"></el-table-column>
  138. <el-table-column width="100" :label="$t('operation')" align="center" header-align="center">
  139. <template slot-scope="scope">
  140. <el-button type="primary" @click="selectedTechPrj(scope.row)">选择</el-button>
  141. </template>
  142. </el-table-column>
  143. </el-table>
  144. </div>
  145. </div>
  146. </div>
  147. </div>
  148. </div>
  149. </div>
  150. </div>
  151. </template>
  152. <script>
  153. import TopHeader from '../components/TopHeader.vue';
  154. import { getTechs, setOpenIApply, setNoOpenIApply, getCreateRepoUser, getCheckRepoName, getTopics } from '~/apis/modules/tech';
  155. export default {
  156. data() {
  157. return {
  158. form: {
  159. type: 'openi',
  160. url: '',
  161. url_err: false,
  162. repo_alias: '',
  163. alias_err: false,
  164. uid: '',
  165. repo_name: '',
  166. name_err: false,
  167. repo_url: '',
  168. topics: [],
  169. topic_err: false,
  170. topicLoading: false,
  171. description: '',
  172. descr_err: false,
  173. tech_search_sel: '0',
  174. tech_search_keyword: '',
  175. tech_obj: null,
  176. tech_show: '',
  177. tech_err: false,
  178. institution: [],
  179. institution_err: false,
  180. },
  181. selectTechPrj: false,
  182. loading: false,
  183. tableData: [],
  184. topicsList: [],
  185. institutionList: [],
  186. ownerList: [],
  187. ownerSelect: {},
  188. submitLoading: false,
  189. };
  190. },
  191. components: {
  192. TopHeader,
  193. },
  194. methods: {
  195. resetData() {
  196. this.form.url = '';
  197. this.form.url_err = false;
  198. this.form.repo_alias = '';
  199. this.form.alias_err = false;
  200. this.form.uid = this.ownerList.length ? this.ownerList[0].value : '';
  201. this.form.repo_name = '';
  202. this.form.repo_url = '';
  203. this.form.name_err = false;
  204. this.form.topics = [];
  205. this.form.topic_err = false;
  206. this.form.topicLoading = false;
  207. this.form.description = '';
  208. this.form.descr_err = false;
  209. this.form.tech_search_sel = '0';
  210. this.form.tech_search_keyword = '';
  211. this.form.tech_obj = null;
  212. this.form.tech_show = '';
  213. this.form.tech_err = false;
  214. this.form.institution = [];
  215. this.form.institution_err = false;
  216. this.tableData = [];
  217. this.topicsList = [];
  218. this.institutionList = [];
  219. this.submitLoading = false;
  220. },
  221. changeType() {
  222. this.resetData();
  223. },
  224. checkUrl() {
  225. this.form.url_err = !this.form.url;
  226. return !this.form.url_err;
  227. },
  228. checkRepoAlias() {
  229. const reg = /^[\u4E00-\u9FA5A-Za-z0-9_.-]{1,100}$/;
  230. const res = reg.test(this.form.repo_alias);
  231. this.form.alias_err = !res;
  232. return res;
  233. },
  234. checkRepoName() {
  235. const reg = /^[A-Za-z0-9_.-]{1,100}$/;
  236. const res = reg.test(this.form.repo_name);
  237. this.form.name_err = !res;
  238. return res;
  239. },
  240. checkTopic() {
  241. this.form.topic_err = this.form.topics.length == 0;
  242. return !this.form.topic_err;
  243. },
  244. checkDescr() {
  245. this.form.descr_err = !this.form.description;
  246. return !this.form.descr_err;
  247. },
  248. checkTech() {
  249. this.form.tech_err = !this.form.tech_obj;
  250. return !this.form.tech_err;
  251. },
  252. checkinstitution() {
  253. this.form.institution_err = this.form.institution.length == 0;
  254. return !this.form.institution_err;
  255. },
  256. changeUrl() {
  257. if (this.form.type == 'openi') return;
  258. const owner = this.ownerSelect.Name;
  259. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  260. const repoValue = this.form.url.match(/^(.*\/)?((.+?)(\.git)?)$/)[3];
  261. this.form.repo_alias = repoValue;
  262. this.checkRepoAlias();
  263. getCheckRepoName({ owner: owner, q: repoValue }).then(res => {
  264. const repo_name = res.data.name;
  265. this.form.repo_name = repo_name;
  266. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  267. this.checkRepoName();
  268. }).catch(err => {
  269. console.log(err);
  270. this.form.repo_name = '';
  271. this.form.repo_url = '';
  272. });
  273. },
  274. changeAlias() {
  275. const owner = this.ownerSelect.Name;
  276. const aliasValue = this.form.repo_alias;
  277. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  278. if (aliasValue && this.checkRepoAlias()) {
  279. getCheckRepoName({ owner: owner, q: aliasValue }).then(res => {
  280. const repo_name = res.data.name;
  281. this.form.repo_name = repo_name;
  282. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  283. }).catch(err => {
  284. console.log(err);
  285. this.form.repo_name = '';
  286. this.form.repo_url = '';
  287. });
  288. } else {
  289. this.form.repo_name = '';
  290. this.form.repo_url = '';
  291. }
  292. },
  293. changeOwner(value) {
  294. this.ownerSelect = this.ownerList.filter(item => item.value == value)[0];
  295. this.form.repo_name && this.changeRepoName();
  296. },
  297. changeRepoName() {
  298. const owner = this.ownerSelect.Name;
  299. const repo_name = this.form.repo_name;
  300. const urlAdd = location.href.split("/")[0] + "//" + location.href.split("/")[2];
  301. if (this.checkRepoName()) {
  302. this.form.repo_url = `${urlAdd}/${owner}/${repo_name}.git`;
  303. } else {
  304. this.form.repo_url = '';
  305. }
  306. },
  307. searchTopics(query) {
  308. if (query !== '') {
  309. this.form.topicLoading = true;
  310. getTopics({ q: query }).then(res => {
  311. this.form.topicLoading = false;
  312. const topics = res.data.topics || [];
  313. this.topicsList = topics.map(item => {
  314. return {
  315. value: item.topic_name,
  316. label: item.topic_name,
  317. }
  318. });
  319. }).catch(err => {
  320. this.topicsList = [];
  321. });
  322. } else {
  323. this.topicsList = [];
  324. }
  325. },
  326. goSelectTechPrj() {
  327. this.form.tech_search_sel = '0';
  328. this.form.tech_search_keyword = '';
  329. this.tableData = [];
  330. this.selectTechPrj = true;
  331. this.searchTechList();
  332. },
  333. clearTechPrj() {
  334. this.form.institution = [];
  335. this.institutionList = [];
  336. this.form.tech_show = '';
  337. this.form.tech_obj = null;
  338. this.form.tech_err = false;
  339. },
  340. goBack() {
  341. this.selectTechPrj = false;
  342. },
  343. searchTechList() {
  344. this.loading = true;
  345. getTechs({
  346. no: this.form.tech_search_sel == '0' ? this.form.tech_search_keyword : '',
  347. institution: this.form.tech_search_sel == '1' ? this.form.tech_search_keyword : '',
  348. name: this.form.tech_search_sel == '2' ? this.form.tech_search_keyword : '',
  349. }).then(res => {
  350. this.loading = false;
  351. res = res.data;
  352. this.tableData = res.data || [];
  353. }).catch(err => {
  354. console.log(err);
  355. this.loading = false;
  356. this.tableData = [];
  357. });
  358. },
  359. selectedTechPrj(item) {
  360. this.form.institution = [];
  361. this.institutionList = item.all_institution.split(',').map((item, index) => {
  362. return {
  363. key: item,
  364. value: item,
  365. }
  366. });
  367. this.form.tech_show = `【${item.no}】 ${item.name}`;
  368. this.form.tech_obj = item;
  369. this.checkTech();
  370. this.goBack();
  371. },
  372. submit() {
  373. const subData = {};
  374. let setApi = null;
  375. if (this.form.type == 'openi') {
  376. setApi = setOpenIApply;
  377. const r1 = this.checkUrl();
  378. const r2 = this.checkTech();
  379. const r3 = this.checkinstitution();
  380. if (r1 && r2 && r3) {
  381. subData.url = this.form.url;
  382. subData.no = this.form.tech_obj.no;
  383. subData.institution = this.form.institution.join(',');
  384. } else {
  385. return;
  386. }
  387. } else {
  388. setApi = setNoOpenIApply;
  389. const r1 = this.checkUrl();
  390. const r2 = this.checkTech();
  391. const r3 = this.checkinstitution();
  392. const r4 = this.checkRepoAlias();
  393. const r5 = this.checkRepoName();
  394. const r6 = this.checkTopic();
  395. const r7 = this.checkDescr();
  396. if (r1 && r2 && r3 && r4 && r5 && r6 && r7) {
  397. subData.url = this.form.url;
  398. subData.uid = this.form.uid;
  399. subData.alias = this.form.repo_alias;
  400. subData.repo_name = this.form.repo_name;
  401. subData.topics = [...this.form.topics];
  402. subData.description = this.form.description.trim();
  403. subData.no = this.form.tech_obj.no;
  404. subData.institution = this.form.institution.join(',');
  405. } else {
  406. return;
  407. }
  408. }
  409. // console.log(subData);
  410. // return;
  411. this.submitLoading = true;
  412. setApi(subData).then(res => {
  413. if (res.data && res.data.code == 0) {
  414. this.$message({
  415. type: 'success',
  416. message: this.$t('submittedSuccessfully'),
  417. });
  418. setTimeout(() => {
  419. window.location.href = '/tech/tech_view';
  420. }, 1000);
  421. } else {
  422. this.submitLoading = false;
  423. this.$message({
  424. type: 'info',
  425. message: res.data.msg
  426. });
  427. }
  428. }).catch(err => {
  429. this.submitLoading = false;
  430. this.$message({
  431. type: 'error',
  432. message: this.$t('submittedFailed'),
  433. });
  434. });
  435. },
  436. cancel() {
  437. window.history.back();
  438. }
  439. },
  440. beforeMount() {
  441. getCreateRepoUser().then(res => {
  442. const data = res.data.Data || [];
  443. this.ownerList = data.map(item => {
  444. return {
  445. value: item.ID,
  446. label: item.FullName || item.Name,
  447. ...item,
  448. }
  449. });
  450. this.ownerSelect = this.ownerList.length ? this.ownerList[0] : {};
  451. this.form.uid = this.ownerList.length ? this.ownerList[0].value : '';
  452. }).catch(err => {
  453. console.log(err);
  454. });
  455. },
  456. mounted() { },
  457. beforeDestroy() { },
  458. };
  459. </script>
  460. <style scoped lang="less">
  461. .title {
  462. text-align: center;
  463. margin: 30px 0;
  464. font-size: 14px;
  465. color: rgb(16, 16, 16);
  466. }
  467. .form-c,
  468. .form-select-tech-prj {
  469. display: flex;
  470. justify-content: center;
  471. .form-wrap {
  472. width: 1000px;
  473. background: rgb(255, 255, 255);
  474. border-color: rgb(212, 212, 213);
  475. border-width: 1px;
  476. border-style: solid;
  477. border-radius: 5px;
  478. box-sizing: border-box;
  479. .form-header {
  480. height: 45px;
  481. background: rgb(240, 240, 240);
  482. border-bottom: 1px solid rgb(212, 212, 213);
  483. color: rgb(16, 16, 16);
  484. padding-left: 16px;
  485. padding-right: 16px;
  486. font-size: 16px;
  487. font-weight: 700;
  488. display: flex;
  489. align-items: center;
  490. justify-content: space-between;
  491. }
  492. .form-content {
  493. margin-top: 40px;
  494. .form-row {
  495. display: flex;
  496. align-items: center;
  497. width: 80%;
  498. margin: 20px auto;
  499. padding-right: 50px;
  500. .row-label {
  501. flex: 2;
  502. text-align: right;
  503. margin-right: 24px;
  504. position: relative;
  505. &.required {
  506. &::after {
  507. position: absolute;
  508. top: -2px;
  509. right: -10px;
  510. content: '*';
  511. color: #db2828;
  512. }
  513. }
  514. &.baseline {
  515. align-self: baseline;
  516. margin-top: 11px;
  517. }
  518. }
  519. .row-content {
  520. flex: 9;
  521. .tips {
  522. margin-top: 4px;
  523. font-size: 14px;
  524. color: rgb(136, 136, 136);
  525. }
  526. }
  527. .reop-url-c {
  528. display: flex;
  529. align-items: center;
  530. }
  531. .openi-repo-url {
  532. color: rgba(16, 16, 16, 1);
  533. }
  534. &.form-row-err {
  535. .row-label {
  536. color: #9f3a38;
  537. }
  538. /deep/ .can-err .el-input__inner,
  539. /deep/ .can-err .el-textarea__inner {
  540. color: #9f3a38;
  541. background: #fff6f6;
  542. border-color: #e0b4b4;
  543. }
  544. }
  545. }
  546. .form-table {
  547. margin-top: 30px;
  548. margin-bottom: 30px;
  549. /deep/ .el-table__header {
  550. th {
  551. background: rgb(249, 249, 249);
  552. font-size: 12px;
  553. color: rgb(136, 136, 136);
  554. font-weight: normal;
  555. }
  556. }
  557. /deep/ .el-table__body {
  558. td {
  559. font-size: 12px;
  560. }
  561. }
  562. /deep/ .el-radio__label {
  563. display: none;
  564. }
  565. }
  566. .form-btn-group {
  567. display: flex;
  568. justify-content: center;
  569. align-items: center;
  570. margin: 30px 0 40px;
  571. }
  572. }
  573. }
  574. }
  575. .btn {
  576. color: rgb(2, 0, 4);
  577. background-color: rgb(194, 199, 204);
  578. border-color: rgb(194, 199, 204);
  579. &.confirm-btn {
  580. color: #fff;
  581. background-color: rgb(56, 158, 13);
  582. border-color: rgb(56, 158, 13);
  583. margin-right: 20px;
  584. }
  585. }
  586. .btn-select {
  587. background-color: rgb(50, 145, 248) !important;
  588. color: white !important;
  589. border-radius: 0 !important;
  590. height: 35px;
  591. }
  592. .owner-sel {
  593. /deep/ .el-input__inner {
  594. padding-left: 48px;
  595. }
  596. }
  597. .owner-item {
  598. display: flex;
  599. align-items: center;
  600. .owner-img {
  601. width: 24px;
  602. height: 24px;
  603. margin-right: 10px;
  604. }
  605. }
  606. </style>