diff --git a/src/config/version.js b/src/config/version.js
index 083c08fb..b9eea9bd 100644
--- a/src/config/version.js
+++ b/src/config/version.js
@@ -124,3 +124,5 @@ export const DELETE_VERSION_STATUS = [
'rejected',
'suspended'
];
+
+export const EDIT_VERSION_STATUS = ['draft', 'rejected'];
diff --git a/src/locales/en/app.json b/src/locales/en/app.json
index a02c1801..8b3f478c 100644
--- a/src/locales/en/app.json
+++ b/src/locales/en/app.json
@@ -116,5 +116,8 @@
"HELM_APP_NAME_TIP": "Beginning with a lowercase letter, up to 14 characters long (supports numbers/lowercase letters/hyphens)",
"MY_INSTANCES_DESCRIPTION": "A list of instances created based on the application.",
+ "VERSION_REVIEW_SUBMIT": "Your review has been submitted successfully.",
+ "VERSION_REVIEW_DESC": " The review includes application service review and platform review. Please pay attention to the review notice.",
+
"TIPS_DELETE_CATE": "After「{{cateName}}」is deleted, applications such as {{appNames}} will be automatically classified into 「uncategorized」"
}
diff --git a/src/locales/zh/app-version.json b/src/locales/zh/app-version.json
index 749ef0fc..805dd70c 100644
--- a/src/locales/zh/app-version.json
+++ b/src/locales/zh/app-version.json
@@ -90,5 +90,11 @@
"Used to describe in detail the specific content of this update": "用于详细描述此次更新的具体内容",
"Pricing information": "定价信息",
"App pricing is not supported for the time being": "暂时不支持应用定价",
- "VERSION_NO_FORMAT_INFO": "输入格式错误,正确格式为小数点和数字组成"
+ "VERSION_NO_FORMAT_INFO": "输入格式错误,正确格式为小数点和数字组成",
+
+ "View version": "查看版本",
+ "VERSION_REVIEW_SUBMIT": "你的审核已提交成功",
+ "VERSION_REVIEW_DESC": "整个审核包括应用服务商审核和平台审核两个环节,请留意审核通知。",
+ "View version package": "查看版本包",
+ "Version Files": "版本文件"
}
diff --git a/src/pages/Dashboard/Apps/Detail/index.jsx b/src/pages/Dashboard/Apps/Detail/index.jsx
index 00444f49..ea3aa987 100644
--- a/src/pages/Dashboard/Apps/Detail/index.jsx
+++ b/src/pages/Dashboard/Apps/Detail/index.jsx
@@ -151,7 +151,7 @@ export default class AppDetail extends Component {
};
renderVersionHandleMenu = item => {
- const { match, t } = this.props;
+ const { user, match, t } = this.props;
const { appId } = match.params;
return (
@@ -164,7 +164,7 @@ export default class AppDetail extends Component {
>
{t('Deploy Instance')}
- {item.status === 'active' && (
+ {item.status === 'active' && user.isAdmin && (
this.openSuspendDialog(item.version_id, 'suspend-version')
}
@@ -172,7 +172,7 @@ export default class AppDetail extends Component {
{t('Suspend version')}
)}
- {item.status === 'suspended' && (
+ {item.status === 'suspended' && user.isAdmin && (
this.openSuspendDialog(item.version_id, 'recover-version')
}
@@ -180,6 +180,14 @@ export default class AppDetail extends Component {
{t('Recover version')}
)}
+
+ {t('View version package')}
+
);
};
@@ -273,10 +281,10 @@ export default class AppDetail extends Component {
}
renderVersions() {
- const { appVersionStore, user, t } = this.props;
+ const { appVersionStore, t } = this.props;
const { versions, isLoading } = appVersionStore;
- let columns = [
+ const columns = [
{
title: t('Version No'),
key: 'name',
@@ -317,9 +325,6 @@ export default class AppDetail extends Component {
)
}
];
- if (!user.isAdmin) {
- columns = columns.filter(item => item.key !== 'actions');
- }
const pagination = {
tableType: 'application',
diff --git a/src/pages/Dashboard/Reviews/Detail/index.jsx b/src/pages/Dashboard/Reviews/Detail/index.jsx
index 4c49467c..11479638 100644
--- a/src/pages/Dashboard/Reviews/Detail/index.jsx
+++ b/src/pages/Dashboard/Reviews/Detail/index.jsx
@@ -96,6 +96,14 @@ export default class ReviewDetail extends Component {
return (
+
+
{t('View version package')}
+
{
+ const {
+ appStore, appVersionStore, match, t
+ } = this.props;
+ const { appId, versionId } = match.params;
+ const { version, packageName, downloadPackage } = appVersionStore;
+ const { appDetail } = appStore;
+ const { status } = version;
+ const isEdit = EDIT_VERSION_STATUS.includes(status);
+ const pkgName = packageName || `${appDetail.name}-${version.name}`;
+
+ return (
+
+ {isEdit && (
+
+ {t('Modify')}
+
+ )}
+ downloadPackage(version.version_id, pkgName)}>
+ {t('Download')}
+
+
+ {t('Package')}
+
+
+ );
+ };
+
renderDeleteDialog = () => {
const { appStore, appVersionStore, t } = this.props;
const { isDialogOpen, hideModal, version } = appVersionStore;
@@ -502,15 +539,12 @@ export default class VersionDetail extends Component {
uploadError,
packageName,
checkPackageFile,
- uploadPackage,
- downloadPackage
+ uploadPackage
} = appVersionStore;
const { appDetail } = appStore;
const isShowUpload = isLoading || Boolean(createError);
const errorFiles = _.keys(uploadError);
-
- const isEdit = version.status === 'draft' || version.status === 'rejected';
const pkgName = packageName || `${appDetail.name}-${version.name}`;
return (
@@ -521,20 +555,11 @@ export default class VersionDetail extends Component {
{t('Upload time')}:
{formatTime(version.status_time, 'YYYY/MM/DD HH:mm:ss')}
-
downloadPackage(version.version_id, pkgName)}
- >
- {t('Download')}
-
- {isEdit && (
-
this.onUploadClick()}
- >
- {t('Modify')}
-
- )}
+
)}
@@ -631,13 +656,13 @@ export default class VersionDetail extends Component {
return (
-
{t('你的应用已提交成功')}
+
{t('VERSION_REVIEW_SUBMIT')}
- {t('整个审核包括应用服务商审核和平台审核两个环节,请留意审核通知。')}
+ {t('VERSION_REVIEW_DESC')}
);
diff --git a/src/pages/Dashboard/Versions/Detail/index.scss b/src/pages/Dashboard/Versions/Detail/index.scss
index 9a7ff64c..092d1c2c 100644
--- a/src/pages/Dashboard/Versions/Detail/index.scss
+++ b/src/pages/Dashboard/Versions/Detail/index.scss
@@ -110,6 +110,16 @@
box-shadow: 0 1px 0 0 $N10;
}
}
+
+ .operation {
+ margin-left: 12px;
+ }
+
+ :global {
+ .operate-menu {
+ text-align: left;
+ }
+ }
}
.updateLog {
diff --git a/src/pages/Dashboard/Versions/Files/index.jsx b/src/pages/Dashboard/Versions/Files/index.jsx
new file mode 100644
index 00000000..459a4c11
--- /dev/null
+++ b/src/pages/Dashboard/Versions/Files/index.jsx
@@ -0,0 +1,142 @@
+import React, { Component } from 'react';
+import { observer, inject } from 'mobx-react';
+import { withTranslation } from 'react-i18next';
+import classnames from 'classnames';
+import _ from 'lodash';
+import { Base64 } from 'js-base64';
+// import CodeMirror from 'react-codemirror';
+
+import { Image, Button } from 'components/Base';
+import Layout, { Grid, Section } from 'components/Layout';
+import { EDIT_VERSION_STATUS } from 'config/version.js';
+
+import styles from './index.scss';
+
+@withTranslation()
+@inject(({ rootStore }) => ({
+ rootStore,
+ appVersionStore: rootStore.appVersionStore,
+ appStore: rootStore.appStore,
+ user: rootStore.user
+}))
+@observer
+export default class VersionDetail extends Component {
+ state = {
+ selectIndex: 0
+ };
+
+ async componentDidMount() {
+ const { appStore, appVersionStore, match } = this.props;
+ const { versionId, appId } = match.params;
+
+ await appStore.fetch(appId);
+ await appVersionStore.fetchPackageFiles(versionId);
+ await appVersionStore.fetch(versionId);
+ }
+
+ changeName = index => {
+ this.setState({
+ selectIndex: index
+ });
+ };
+
+ changeFile = e => {
+ const value = e.target.value;
+ const { appVersionStore } = this.props;
+ const { allFiles } = appVersionStore;
+ const { selectIndex } = this.state;
+ const file = allFiles[selectIndex];
+ allFiles.splice(selectIndex, 1, {
+ name: file.name,
+ content: value
+ });
+ };
+
+ modifyFile = () => {
+ const { appVersionStore } = this.props;
+ const { selectIndex } = this.state;
+ const { version, allFiles, modifyPackageFiles } = appVersionStore;
+ const file = allFiles[selectIndex];
+ modifyPackageFiles(version.version_id, {
+ [file.name]: Base64.encode(file.content)
+ });
+ };
+
+ renderFileNames() {
+ const { appVersionStore } = this.props;
+ const { allFiles } = appVersionStore;
+ const { selectIndex } = this.state;
+
+ return (
+
+
+ {allFiles.map((item, index) => (
+ - this.changeName(index)}
+ className={classnames({ [styles.active]: index === selectIndex })}
+ >
+ {item.name}
+
+ ))}
+
+
+ );
+ }
+
+ render() {
+ const {
+ appStore, appVersionStore, user, t
+ } = this.props;
+ const { version, allFiles } = appVersionStore;
+ const { appDetail } = appStore;
+ const { selectIndex } = this.state;
+ const isEdit = EDIT_VERSION_STATUS.includes(version.status) && user.isDevPortal;
+
+ return (
+
+
+
+
+
+
+ {appDetail.name} / {version.name}
+
+ {isEdit && (
+
+ )}
+
+
+ {this.renderFileNames()}
+
+
+
+ );
+ }
+}
diff --git a/src/pages/Dashboard/Versions/Files/index.scss b/src/pages/Dashboard/Versions/Files/index.scss
new file mode 100644
index 00000000..c7e0da60
--- /dev/null
+++ b/src/pages/Dashboard/Versions/Files/index.scss
@@ -0,0 +1,94 @@
+@import '~scss/vars';
+
+.versionFiles {
+ .container {
+ border: 1px solid $N10;
+ }
+
+ .header {
+ padding: 10px 20px;
+ line-height: 48px;
+ font-size: 16px;
+ font-weight: 500;
+ color: $N500;
+ background-color: $N0;
+ border: 1px solid $N10;
+ border-bottom: 0 none;
+
+ .name {
+ @include title-font;
+ margin-bottom: 24px;
+ }
+
+ .image {
+ display: inline-block;
+ margin-right: 12px;
+ width: 48px;
+ height: 48px;
+ line-height: 48px;
+ }
+ }
+}
+
+.fileNames {
+ width: calc(calc(100% + 20px) / 12 * 3 + 20px) !important;
+ border-right: 1px solid $N10;
+
+ li {
+ box-sizing: border-box;
+ height: 44px;
+ padding: 11px 20px;
+ line-height: 20px;
+ font-size: 12px;
+ align-items: center;
+ cursor: pointer;
+
+ &.active {
+ margin-right: -2px;
+ background-color: $N0;
+ box-shadow: 0 1px 0 0 $N10, 0 -1px 0 0 $N10;
+ color: $N500;
+ font-weight: 500;
+ }
+
+ &:hover {
+ background-color: $N0;
+ box-shadow: 0 1px 0 0 $N10, 0 -1px 0 0 $N10;
+ }
+
+ .name {
+ @include textCut;
+ display: inline-block;
+ max-width: 200px;
+ }
+ }
+
+ .total {
+ float: right;
+ color: $N300;
+ flex: 1;
+ }
+}
+
+.fileContent {
+ background-color: $N0;
+ textarea,
+ .code {
+ padding: 20px;
+ width: 100%;
+ height: calc(100vh - 100px);
+ min-height: 500px;
+ line-height: 24px;
+ border: 0 none;
+ color: $N300;
+
+ &:focus {
+ color: $N500;
+ }
+ }
+
+ .readOnly:focus {
+ box-shadow: none;
+ color: $N300;
+ }
+}
diff --git a/src/pages/Dashboard/index.js b/src/pages/Dashboard/index.js
index 38ff0065..77a37f8a 100644
--- a/src/pages/Dashboard/index.js
+++ b/src/pages/Dashboard/index.js
@@ -6,6 +6,7 @@ export MyApps from './Apps/MyApps';
export Audits from './Apps/Audits';
export Versions from './Versions';
export VersionDetail from './Versions/Detail';
+export VersionFiles from './Versions/Files';
export AppInfo from './Apps/AppInfo';
export Reviews from './Reviews';
diff --git a/src/routes/names.js b/src/routes/names.js
index 6742c0f7..f98fd445 100644
--- a/src/routes/names.js
+++ b/src/routes/names.js
@@ -18,6 +18,7 @@ export default {
runtimes: '/:portal/runtimes',
runtimeCreate: '/:portal/runtimes/create',
deploy: '/:portal/apps/:appId/deploy/:versionId?',
+ versionFiles: '/:portal/apps/:appId/versionFiles/:versionId',
// partial common routes
clusters: '/:portal/clusters',
diff --git a/src/routes/portals/admin.jsx b/src/routes/portals/admin.jsx
index da1c2786..8073962c 100644
--- a/src/routes/portals/admin.jsx
+++ b/src/routes/portals/admin.jsx
@@ -20,6 +20,7 @@ import {
Reviews,
ReviewDetail,
AppDeploy,
+ VersionFiles,
CloudEnv,
CloudInfo,
NotificationServer
@@ -42,6 +43,10 @@ export default ({ prefix }) => (
path={`${prefix}/apps/:appId/deploy/:versionId?`}
component={AppDeploy}
/>
+
diff --git a/src/routes/portals/dev.jsx b/src/routes/portals/dev.jsx
index dd2615f9..ee5abd14 100644
--- a/src/routes/portals/dev.jsx
+++ b/src/routes/portals/dev.jsx
@@ -11,6 +11,7 @@ import {
AppAdd,
Versions,
VersionDetail,
+ VersionFiles,
AppDeploy,
Audits,
AppInfo,
@@ -38,6 +39,10 @@ export default ({ prefix }) => (
path={`${prefix}/apps/:appId/versions/:versionId`}
component={VersionDetail}
/>
+
(
component={AppDeploy}
/>
+
+
);
diff --git a/src/stores/app/version.js b/src/stores/app/version.js
index 71d967b4..42380be6 100644
--- a/src/stores/app/version.js
+++ b/src/stores/app/version.js
@@ -68,7 +68,7 @@ export default class AppVersionStore extends Store {
this.uploadError = {};
- this.createResult = null;
+ this.requestResult = null;
this.activeType = 'unprocessed';
@@ -97,6 +97,8 @@ export default class AppVersionStore extends Store {
this.checkResult = {};
this.reveiwTypes = [];
+
+ this.allFiles = [];
});
}
@@ -258,14 +260,14 @@ export default class AppVersionStore extends Store {
await this.create(assign(defaultParams, params));
}
- if (get(this.createResult, 'version_id')) {
+ if (get(this.requestResult, 'version_id')) {
if (!versionId) {
await this.fetchAll();
this.createStep = 2; // show application has been created page
}
return true;
}
- const { err, errDetail } = this.createResult;
+ const { err, errDetail } = this.requestResult;
this.createError = errDetail || err;
return false;
};
@@ -273,14 +275,14 @@ export default class AppVersionStore extends Store {
@action
create = async (params = {}) => {
this.isLoading = true;
- this.createResult = await this.request.post('app_versions', params);
+ this.requestResult = await this.request.post('app_versions', params);
this.isLoading = false;
};
@action
modify = async (params = {}) => {
this.isLoading = true;
- this.createResult = await this.request.patch('app_versions', params);
+ this.requestResult = await this.request.patch('app_versions', params);
this.isLoading = false;
};
@@ -540,15 +542,27 @@ export default class AppVersionStore extends Store {
@action
fetchPackageFiles = async versionId => {
- const result = await this.request.get(`app_version/package/files`, {
- version_id: versionId,
- files: ['README.md']
+ const result = await this.request.get('app_version/package/files', {
+ version_id: versionId
});
const files = get(result, 'files', {});
- if (files['README.md']) {
- this.readme = Base64.decode(files['README.md']);
- } else {
- this.readme = '';
+
+ this.allFiles = _.map(files, (item, key) => ({
+ name: key,
+ content: Base64.decode(item)
+ })).filter(item => !item.name.includes('._') && !item.name.startsWith('.'));
+ };
+
+ @action
+ modifyPackageFiles = async (versionId, packageFiles) => {
+ const data = {
+ version_id: versionId,
+ package_files: packageFiles
+ };
+ await this.modify(data);
+
+ if (get(this.requestResult, 'version_id')) {
+ this.success('Save successful');
}
};