Merge branch 'develop' into develop

This commit is contained in:
langren1353 2022-03-20 03:50:13 +08:00 committed by GitHub
commit 66d6c2f13c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 239 additions and 193 deletions

View File

@ -1,59 +0,0 @@
name: Build QL BASE
on:
schedule:
- cron: '00 18 * * *' # GMT 14:00 => 北京时间 2:00
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: '14'
- name: Set time zone
uses: szenius/set-timezone@v1.0
with:
timezoneLinux: "Asia/Shanghai"
timezoneMacos: "Asia/Shanghai"
timezoneWindows: "China Standard Time"
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login to GHCR
uses: docker/login-action@v1
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
build-args: |
MAINTAINER=${{ github.repository_owner }}
QL_BRANCH=${{ github.ref_name }}
platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64
context: .
file: docker/base/Dockerfile
push: true
tags: whyour/ql:base

View File

@ -16,6 +16,10 @@ export default defineConfig({
}, },
favicon: '/images/g5.ico', favicon: '/images/g5.ico',
proxy: { proxy: {
'/api/public': {
target: 'http://127.0.0.1:5400/',
changeOrigin: true,
},
'/api': { '/api': {
target: 'http://127.0.0.1:5600/', target: 'http://127.0.0.1:5600/',
changeOrigin: true, changeOrigin: true,

View File

@ -44,6 +44,27 @@
# 待完善 # 待完善
``` ```
### podman 部署
1. podman 安装
```bash
https://podman.io/getting-started/installation
```
2. 启动容器
```bash
podman run -dit \
--network bridge \
-v $PWD/ql:/ql/data \
-p 5700:5700 \
--name qinglong \
--hostname qinglong \
--restart unless-stopped \
docker.io/whyour/qinglong:latest
```
### docker 部署 ### docker 部署
1. docker 安装 1. docker 安装

View File

@ -1,4 +1,5 @@
FROM whyour/ql:base FROM node:alpine
ARG QL_MAINTAINER="whyour" ARG QL_MAINTAINER="whyour"
LABEL maintainer="${QL_MAINTAINER}" LABEL maintainer="${QL_MAINTAINER}"
ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git ARG QL_URL=https://github.com/${QL_MAINTAINER}/qinglong.git
@ -13,17 +14,45 @@ ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
WORKDIR ${QL_DIR} WORKDIR ${QL_DIR}
RUN git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \ RUN set -x \
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update -f \
&& apk upgrade \
&& apk --no-cache add -f bash \
coreutils \
moreutils \
git \
curl \
wget \
tzdata \
perl \
openssl \
nginx \
python3 \
jq \
openssh \
py3-pip \
&& rm -rf /var/cache/apk/* \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& touch ~/.bashrc \
&& git config --global user.email "qinglong@@users.noreply.github.com" \
&& git config --global user.name "qinglong" \
&& npm install -g pnpm \
&& pnpm install -g pm2 \
&& pnpm install -g ts-node typescript tslib \
&& git clone -b ${QL_BRANCH} ${QL_URL} ${QL_DIR} \
&& cd ${QL_DIR} \ && cd ${QL_DIR} \
&& cp -f .env.example .env \ && cp -f .env.example .env \
&& chmod 777 ${QL_DIR}/shell/*.sh \ && chmod 777 ${QL_DIR}/shell/*.sh \
&& chmod 777 ${QL_DIR}/docker/*.sh \ && chmod 777 ${QL_DIR}/docker/*.sh \
&& cp -rf /node_modules ./ \
&& rm -rf /node_modules \
&& pnpm install --prod \ && pnpm install --prod \
&& rm -rf /root/.pnpm-store \ && rm -rf /root/.pnpm-store \
&& rm -rf /root/.cache \ && rm -rf /root/.cache \
&& rm -rf /root/.npm \
&& git clone -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \ && git clone -b ${QL_BRANCH} https://github.com/${QL_MAINTAINER}/qinglong-static.git /static \
&& cp -rf /static/* ${QL_DIR} \ && mkdir -p ${QL_DIR}/static \
&& cp -rf /static/* ${QL_DIR}/static \
&& rm -rf /static && rm -rf /static
ENTRYPOINT ["./docker/docker-entrypoint.sh"] ENTRYPOINT ["./docker/docker-entrypoint.sh"]

View File

@ -1,49 +0,0 @@
FROM node:alpine
ARG QL_MAINTAINER="whyour"
LABEL maintainer="${QL_MAINTAINER}"
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
LANG=zh_CN.UTF-8 \
SHELL=/bin/bash \
PS1="\u@\h:\w \$ "
COPY package.json /
RUN set -x \
&& sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories \
&& apk update -f \
&& apk upgrade \
&& apk --no-cache add -f bash \
coreutils \
moreutils \
git \
curl \
wget \
tzdata \
perl \
openssl \
nginx \
python3 \
jq \
openssh \
py3-pip \
python2 \
g++ \
make \
&& rm -rf /var/cache/apk/* \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
&& touch ~/.bashrc \
&& git config --global user.email "qinglong@@users.noreply.github.com" \
&& git config --global user.name "qinglong" \
&& npm install -g pnpm \
&& pnpm install -g pm2 \
&& pnpm install -g ts-node typescript tslib \
&& cd / && pnpm install --prod \
&& apk --purge del python2 g++ make \
&& rm -rf /root/.npm \
&& rm -rf /root/.pnpm-store \
&& rm -rf /root/.cache \
&& rm -f /package.json
CMD [ "node" ]

View File

@ -4,10 +4,12 @@
"start": "concurrently -n w: npm:start:*", "start": "concurrently -n w: npm:start:*",
"start:front": "umi dev", "start:front": "umi dev",
"start:back": "nodemon", "start:back": "nodemon",
"start:public": "ts-node back/public.ts",
"build:front": "umi build", "build:front": "umi build",
"build:back": "tsc -p tsconfig.back.json", "build:back": "tsc -p tsconfig.back.json",
"panel": "npm run build:back && node static/build/app.js", "panel": "npm run build:back && node static/build/app.js",
"schedule": "npm run build:back && node static/build/schedule.js", "schedule": "npm run build:back && node static/build/schedule.js",
"public": "npm run build:back && node static/build/public.js",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"prepare": "umi generate tmp", "prepare": "umi generate tmp",
"test": "umi-test", "test": "umi-test",

View File

@ -22,9 +22,9 @@ if [[ ! -d ${repo_path}/.git ]]; then
git_clone_scripts ${url} ${repo_path} "main" git_clone_scripts ${url} ${repo_path} "main"
fi fi
cp -rf "$repo_path/jbot" $dir_root cp -rf "$repo_path/jbot" $dir_data
if [[ ! -f "$dir_root/config/bot.json" ]]; then if [[ ! -f "$dir_config/bot.json" ]]; then
cp -f "$repo_path/config/bot.json" "$dir_root/config" cp -f "$repo_path/config/bot.json" "$dir_config"
fi fi
echo -e "\nbot文件下载成功...\n" echo -e "\nbot文件下载成功...\n"
@ -32,9 +32,9 @@ echo -e "3、安装python3依赖...\n"
if [[ $PipMirror ]]; then if [[ $PipMirror ]]; then
pip3 config set global.index-url $PipMirror pip3 config set global.index-url $PipMirror
fi fi
cp -f "$repo_path/jbot/requirements.txt" "$dir_root" cp -f "$repo_path/jbot/requirements.txt" "$dir_data"
cd $dir_root cd $dir_data
cat requirements.txt | while read LREAD cat requirements.txt | while read LREAD
do do
if [[ ! $(pip3 show "${LREAD%%=*}" 2>/dev/null) ]]; then if [[ ! $(pip3 show "${LREAD%%=*}" 2>/dev/null) ]]; then
@ -46,7 +46,7 @@ echo -e "\npython3依赖安装成功...\n"
echo -e "4、启动bot程序...\n" echo -e "4、启动bot程序...\n"
make_dir $dir_log/bot make_dir $dir_log/bot
cd $dir_root cd $dir_data
ps -ef | grep "python3 -m jbot" | grep -v grep | awk '{print $1}' | xargs kill -9 2>/dev/null ps -ef | grep "python3 -m jbot" | grep -v grep | awk '{print $1}' | xargs kill -9 2>/dev/null
nohup python3 -m jbot >$dir_log/bot/nohup.log 2>&1 & nohup python3 -m jbot >$dir_log/bot/nohup.log 2>&1 &
echo -e "bot启动成功...\n" echo -e "bot启动成功...\n"

View File

@ -26,6 +26,7 @@
.monaco-editor:not(.rename-box) { .monaco-editor:not(.rename-box) {
height: calc(100vh - 128px) !important; height: calc(100vh - 128px) !important;
height: calc(100vh - var(--vh-offset, 0px) - 128px) !important; height: calc(100vh - var(--vh-offset, 0px) - 128px) !important;
.view-overlays .current-line { .view-overlays .current-line {
border-width: 0; border-width: 0;
} }
@ -33,6 +34,7 @@
.rename-box { .rename-box {
height: 0; height: 0;
.rename-input { .rename-input {
height: 0; height: 0;
padding: 0 !important; padding: 0 !important;
@ -43,6 +45,7 @@
.ant-pro-grid-content.wide { .ant-pro-grid-content.wide {
max-width: unset !important; max-width: unset !important;
overflow: auto; overflow: auto;
.ant-pro-page-container-children-content { .ant-pro-page-container-children-content {
overflow: auto; overflow: auto;
height: 100%; height: 100%;
@ -57,6 +60,7 @@
.ant-tooltip { .ant-tooltip {
max-width: 500px !important; max-width: 500px !important;
.ant-tooltip-inner { .ant-tooltip-inner {
word-break: break-all !important; word-break: break-all !important;
} }
@ -72,6 +76,7 @@
.log-select { .log-select {
width: 250px; width: 250px;
} }
.ant-page-header-heading-left { .ant-page-header-heading-left {
min-width: 100px; min-width: 100px;
} }
@ -81,6 +86,7 @@
.config-select { .config-select {
width: 250px; width: 250px;
} }
.ant-page-header-heading-left { .ant-page-header-heading-left {
min-width: 100px; min-width: 100px;
} }
@ -119,10 +125,13 @@
flex-direction: column; flex-direction: column;
height: calc(100vh - 48px); height: calc(100vh - 48px);
height: calc(100vh - var(--vh-offset, 0px) - 48px); height: calc(100vh - var(--vh-offset, 0px) - 48px);
.ant-pro-grid-content.wide { .ant-pro-grid-content.wide {
flex: 1; flex: 1;
.ant-pro-grid-content-children { .ant-pro-grid-content-children {
height: calc(100% - 36px); height: calc(100% - 36px);
> div, > div,
.log-container, .log-container,
.react-codemirror2, .react-codemirror2,
@ -197,6 +206,7 @@
.Resizer.disabled { .Resizer.disabled {
cursor: not-allowed; cursor: not-allowed;
} }
.Resizer.disabled:hover { .Resizer.disabled:hover {
border-color: transparent; border-color: transparent;
} }
@ -209,6 +219,7 @@
.inline-countdown.ant-statistic { .inline-countdown.ant-statistic {
display: inline-block; display: inline-block;
.ant-statistic-content { .ant-statistic-content {
font-size: 14px; font-size: 14px;
padding: 0 3px; padding: 0 3px;
@ -253,6 +264,7 @@
.ant-pro-basicLayout-content { .ant-pro-basicLayout-content {
margin: 18px; margin: 18px;
.ant-pro-page-container { .ant-pro-page-container {
margin: -18px -18px -18px; margin: -18px -18px -18px;
} }
@ -280,25 +292,27 @@
.side-menu-user-drop-menu { .side-menu-user-drop-menu {
position: relative; position: relative;
margin: 0;
padding: 4px 0;
text-align: left; text-align: left;
background-clip: padding-box;
outline: none; outline: none;
background-color: #fff; padding: 4px 0;
border: 1px solid rgba(255, 255, 255, 0.12);
border-radius: 4px; border-radius: 4px;
box-shadow: 0 6px 16px -8px rgb(0 0 0 / 8%), 0 9px 28px 0 rgb(0 0 0 / 5%), box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
0 12px 48px 16px rgb(0 0 0 / 3%); overflow: auto;
.ant-dropdown-menu-item:hover { background-color: #fff;
color: #1890ff; }
}
[data-dark='true'] .side-menu-user-drop-menu {
background-color: #373739;
} }
.ant-pro-sider-logo { .ant-pro-sider-logo {
padding: 16px 8px !important; padding: 16px 8px !important;
h1 { h1 {
margin-left: 5px !important; margin-left: 5px !important;
} }
img { img {
width: 32px !important; width: 32px !important;
border-radius: 52% !important; border-radius: 52% !important;

View File

@ -272,7 +272,7 @@ export default function (props: any) {
collapsed={collapsed} collapsed={collapsed}
rightContentRender={() => rightContentRender={() =>
ctx.isPhone && ( ctx.isPhone && (
<Dropdown overlay={menu} trigger={['click']}> <Dropdown overlay={menu} placement="bottomRight" trigger={['click']}>
<span className="side-menu-user-wrapper"> <span className="side-menu-user-wrapper">
<Avatar shape="square" size="small" icon={<UserOutlined />} /> <Avatar shape="square" size="small" icon={<UserOutlined />} />
<span style={{ marginLeft: 5 }}>admin</span> <span style={{ marginLeft: 5 }}>admin</span>
@ -289,7 +289,7 @@ export default function (props: any) {
}} }}
> >
{!collapsed && !ctx.isPhone && ( {!collapsed && !ctx.isPhone && (
<Dropdown overlay={menu} trigger={['hover']}> <Dropdown overlay={menu} placement="topLeft" trigger={['hover']}>
<span className="side-menu-user-wrapper"> <span className="side-menu-user-wrapper">
<Avatar shape="square" size="small" icon={<UserOutlined />} /> <Avatar shape="square" size="small" icon={<UserOutlined />} />
<span style={{ marginLeft: 5 }}>admin</span> <span style={{ marginLeft: 5 }}>admin</span>

View File

@ -50,11 +50,13 @@ const CronDetailModal = ({
handleCancel, handleCancel,
visible, visible,
theme, theme,
isPhone,
}: { }: {
cron?: any; cron?: any;
visible: boolean; visible: boolean;
handleCancel: (needUpdate?: boolean) => void; handleCancel: (needUpdate?: boolean) => void;
theme: string; theme: string;
isPhone: boolean;
}) => { }) => {
const [activeTabKey, setActiveTabKey] = useState('log'); const [activeTabKey, setActiveTabKey] = useState('log');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -64,6 +66,8 @@ const CronDetailModal = ({
const [isLogModalVisible, setIsLogModalVisible] = useState(false); const [isLogModalVisible, setIsLogModalVisible] = useState(false);
const editorRef = useRef<any>(null); const editorRef = useRef<any>(null);
const [scriptInfo, setScriptInfo] = useState<any>({}); const [scriptInfo, setScriptInfo] = useState<any>({});
const [logUrl, setLogUrl] = useState('');
const [validTabs, setValidTabs] = useState(tabList);
const contentList: any = { const contentList: any = {
log: ( log: (
@ -99,6 +103,8 @@ const CronDetailModal = ({
}; };
const onClickItem = (item: LogItem) => { const onClickItem = (item: LogItem) => {
localStorage.setItem('logCron', cron.id);
setLogUrl(`${config.apiPrefix}logs/${item.directory}/${item.filename}`);
request request
.get(`${config.apiPrefix}logs/${item.directory}/${item.filename}`) .get(`${config.apiPrefix}logs/${item.directory}/${item.filename}`)
.then((data) => { .then((data) => {
@ -126,6 +132,7 @@ const CronDetailModal = ({
const getScript = () => { const getScript = () => {
const cmd = cron.command.split(' ') as string[]; const cmd = cron.command.split(' ') as string[];
if (cmd[0] === 'task') { if (cmd[0] === 'task') {
setValidTabs(validTabs);
if (cmd[1].startsWith('/ql/data/scripts')) { if (cmd[1].startsWith('/ql/data/scripts')) {
cmd[1] = cmd[1].replace('/ql/data/scripts/', ''); cmd[1] = cmd[1].replace('/ql/data/scripts/', '');
} }
@ -141,6 +148,8 @@ const CronDetailModal = ({
.then((data) => { .then((data) => {
setValue(data.data); setValue(data.data);
}); });
} else {
setValidTabs([validTabs[0]]);
} }
}; };
@ -171,6 +180,7 @@ const CronDetailModal = ({
}) })
.then((_data: any) => { .then((_data: any) => {
if (_data.code === 200) { if (_data.code === 200) {
setValue(content);
message.success(`保存成功`); message.success(`保存成功`);
} else { } else {
message.error(_data); message.error(_data);
@ -198,7 +208,9 @@ const CronDetailModal = ({
title={ title={
<> <>
<span>{cron.name}</span> <span>{cron.name}</span>
<Divider type="vertical"></Divider> {cron.labels?.length > 0 && cron.labels[0] !== '' && (
<Divider type="vertical"></Divider>
)}
{cron.labels?.length > 0 && {cron.labels?.length > 0 &&
cron.labels[0] !== '' && cron.labels[0] !== '' &&
cron.labels?.map((label: string, i: number) => ( cron.labels?.map((label: string, i: number) => (
@ -213,11 +225,17 @@ const CronDetailModal = ({
forceRender forceRender
footer={false} footer={false}
onCancel={() => handleCancel()} onCancel={() => handleCancel()}
width={'80vw'} wrapClassName="crontab-detail"
bodyStyle={{ background: '#eee', padding: 12 }} width={!isPhone ? '80vw' : ''}
> >
<div style={{ height: '80vh' }}> <div className="card-wrapper">
<Card bodyStyle={{ display: 'flex', justifyContent: 'space-between' }}> <Card>
<div className="cron-detail-info-item">
<div className="cron-detail-info-title"></div>
<div className="cron-detail-info-value">{cron.command}</div>
</div>
</Card>
<Card style={{ marginTop: 10 }}>
<div className="cron-detail-info-item"> <div className="cron-detail-info-item">
<div className="cron-detail-info-title"></div> <div className="cron-detail-info-title"></div>
<div className="cron-detail-info-value"> <div className="cron-detail-info-value">
@ -250,10 +268,6 @@ const CronDetailModal = ({
)} )}
</div> </div>
</div> </div>
<div className="cron-detail-info-item">
<div className="cron-detail-info-title"></div>
<div className="cron-detail-info-value">{cron.command}</div>
</div>
<div className="cron-detail-info-item"> <div className="cron-detail-info-item">
<div className="cron-detail-info-title"></div> <div className="cron-detail-info-title"></div>
<div className="cron-detail-info-value">{cron.schedule}</div> <div className="cron-detail-info-value">{cron.schedule}</div>
@ -289,8 +303,8 @@ const CronDetailModal = ({
</div> </div>
</Card> </Card>
<Card <Card
style={{ marginTop: 16 }} style={{ marginTop: 10 }}
tabList={tabList} tabList={validTabs}
activeTabKey={activeTabKey} activeTabKey={activeTabKey}
onTabChange={(key) => { onTabChange={(key) => {
onTabChange(key); onTabChange(key);
@ -306,7 +320,6 @@ const CronDetailModal = ({
</Button> </Button>
) )
} }
bodyStyle={{ height: 'calc(80vh - 188px)', overflowY: 'auto' }}
> >
{contentList[activeTabKey]} {contentList[activeTabKey]}
</Card> </Card>
@ -318,6 +331,7 @@ const CronDetailModal = ({
}} }}
cron={cron} cron={cron}
data={log} data={log}
logUrl={logUrl}
/> />
</Modal> </Modal>
); );

View File

@ -8,15 +8,74 @@
} }
} }
.cron-detail-info-item { .crontab-detail {
flex: auto; .card-wrapper {
height: 80vh;
height: calc(80vh - var(--vh-offset, 0px));
.cron-detail-info-title { .ant-card:last-child {
color: #888; .ant-card-body {
height: calc(80vh - 238px);
height: calc(80vh - var(--vh-offset, 0px) - 238px);
overflow-y: auto;
}
}
} }
.cron-detail-info-value { .ant-modal-body {
margin-top: 18px; background: #eee;
padding: 12px;
}
.ant-card-body {
padding: 18px;
}
.ant-card-head {
padding: 0 18px;
}
.ant-card:first-child {
max-height: 66px;
overflow: auto;
.ant-card-body {
min-width: 600px;
}
.cron-detail-info-item {
display: flex;
.cron-detail-info-title {
width: 50px;
}
.cron-detail-info-value {
flex: 1;
margin-top: 0;
}
}
}
.ant-card:nth-child(2) {
overflow-x: auto;
.ant-card-body {
display: flex;
justify-content: space-between;
min-width: 600px;
}
}
.cron-detail-info-item {
flex: auto;
.cron-detail-info-title {
color: #888;
}
.cron-detail-info-value {
margin-top: 12px;
}
} }
} }

View File

@ -40,7 +40,7 @@ import { getTableScroll } from '@/utils/index';
import { history } from 'umi'; import { history } from 'umi';
import './index.less'; import './index.less';
const { Text } = Typography; const { Text, Paragraph } = Typography;
const { Search } = Input; const { Search } = Input;
export enum CrontabStatus { export enum CrontabStatus {
@ -85,28 +85,32 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
goToScriptManager(record); goToScriptManager(record);
}} }}
> >
<Popover {record.labels?.length > 0 && record.labels[0] !== '' ? (
placement="right" <Popover
trigger={isPhone ? 'click' : 'hover'} placement="right"
content={ trigger={isPhone ? 'click' : 'hover'}
<div> content={
{record.labels?.map((label: string) => ( <div>
<Tag {record.labels?.map((label: string) => (
color="blue" <Tag
style={{ cursor: 'point' }} color="blue"
onClick={(e) => { style={{ cursor: 'point' }}
e.stopPropagation(); onClick={(e) => {
setSearchText(`label:${label}`); e.stopPropagation();
}} setSearchValue(`label:${label}`);
> }}
<a>{label}</a> >
</Tag> <a>{label}</a>
))} </Tag>
</div> ))}
} </div>
> }
{record.name || '-'} >
</Popover> {record.name || '-'}
</Popover>
) : (
record.name || '-'
)}
{record.isPinned ? ( {record.isPinned ? (
<span> <span>
<PushpinOutlined /> <PushpinOutlined />
@ -130,16 +134,16 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
align: 'center' as const, align: 'center' as const,
render: (text: string, record: any) => { render: (text: string, record: any) => {
return ( return (
<span <Paragraph
style={{ style={{
textAlign: 'left',
width: '100%',
display: 'inline-block',
wordBreak: 'break-all', wordBreak: 'break-all',
marginBottom: 0,
textAlign: 'left',
}} }}
ellipsis={{ tooltip: text, rows: 2 }}
> >
{text} {text}
</span> </Paragraph>
); );
}, },
sorter: { sorter: {
@ -364,6 +368,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
const [tableScrollHeight, setTableScrollHeight] = useState<number>(); const [tableScrollHeight, setTableScrollHeight] = useState<number>();
const [isDetailModalVisible, setIsDetailModalVisible] = useState(false); const [isDetailModalVisible, setIsDetailModalVisible] = useState(false);
const [detailCron, setDetailCron] = useState<any>(); const [detailCron, setDetailCron] = useState<any>();
const [searchValue, setSearchValue] = useState('');
const goToScriptManager = (record: any) => { const goToScriptManager = (record: any) => {
const cmd = record.command.split(' ') as string[]; const cmd = record.command.split(' ') as string[];
@ -627,7 +632,8 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
index: number; index: number;
}> = ({ record, index }) => ( }> = ({ record, index }) => (
<Dropdown <Dropdown
arrow arrow={{ pointAtCenter: true }}
placement="bottomRight"
trigger={['click']} trigger={['click']}
overlay={ overlay={
<Menu <Menu
@ -842,7 +848,8 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
enterButton enterButton
allowClear allowClear
loading={loading} loading={loading}
value={searchText} value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
onSearch={onSearch} onSearch={onSearch}
/>, />,
<Button key="2" type="primary" onClick={() => addCron()}> <Button key="2" type="primary" onClick={() => addCron()}>
@ -968,6 +975,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
}} }}
cron={detailCron} cron={detailCron}
theme={theme} theme={theme}
isPhone={isPhone}
/> />
</PageContainer> </PageContainer>
); );

View File

@ -21,11 +21,13 @@ const CronLogModal = ({
handleCancel, handleCancel,
visible, visible,
data, data,
logUrl,
}: { }: {
cron?: any; cron?: any;
visible: boolean; visible: boolean;
handleCancel: () => void; handleCancel: () => void;
data?: string; data?: string;
logUrl?: string;
}) => { }) => {
const [value, setValue] = useState<string>('启动中...'); const [value, setValue] = useState<string>('启动中...');
const [loading, setLoading] = useState<any>(true); const [loading, setLoading] = useState<any>(true);
@ -38,7 +40,7 @@ const CronLogModal = ({
setLoading(true); setLoading(true);
} }
request request
.get(`${config.apiPrefix}crons/${cron.id}/log`) .get(logUrl ? logUrl : `${config.apiPrefix}crons/${cron.id}/log`)
.then((data: any) => { .then((data: any) => {
if (localStorage.getItem('logCron') === String(cron.id)) { if (localStorage.getItem('logCron') === String(cron.id)) {
const log = data.data as string; const log = data.data as string;
@ -99,10 +101,10 @@ const CronLogModal = ({
}; };
useEffect(() => { useEffect(() => {
if (cron && cron.id) { if (cron && cron.id && visible) {
getCronLog(true); getCronLog(true);
} }
}, [cron]); }, [cron, visible]);
useEffect(() => { useEffect(() => {
if (data) { if (data) {

View File

@ -32,7 +32,7 @@ import './index.less';
import { getTableScroll } from '@/utils/index'; import { getTableScroll } from '@/utils/index';
import { doc } from 'prettier'; import { doc } from 'prettier';
const { Text } = Typography; const { Text, Paragraph } = Typography;
const { Search, TextArea } = Input; const { Search, TextArea } = Input;
enum Status { enum Status {
@ -124,18 +124,18 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
key: 'value', key: 'value',
align: 'center' as const, align: 'center' as const,
width: '35%', width: '35%',
ellipsis: {
showTitle: false,
},
render: (text: string, record: any) => { render: (text: string, record: any) => {
return ( return (
<Tooltip <Paragraph
placement="topLeft" style={{
title={text} wordBreak: 'break-all',
trigger={['hover', 'click']} marginBottom: 0,
textAlign: 'left',
}}
ellipsis={{ tooltip: text, rows: 2 }}
> >
<span>{text}</span> {text}
</Tooltip> </Paragraph>
); );
}, },
}, },

View File

@ -177,7 +177,7 @@ const CheckUpdate = ({ socketMessage }: any) => {
</span> </span>
), ),
duration: 10, duration: 30,
}); });
setTimeout(() => { setTimeout(() => {
window.location.reload(); window.location.reload();

View File

@ -120,7 +120,7 @@ const Setting = ({
const [theme, setTheme] = useState(defaultDarken); const [theme, setTheme] = useState(defaultDarken);
const [dataSource, setDataSource] = useState<any[]>([]); const [dataSource, setDataSource] = useState<any[]>([]);
const [isModalVisible, setIsModalVisible] = useState(false); const [isModalVisible, setIsModalVisible] = useState(false);
const [editedApp, setEditedApp] = useState(); const [editedApp, setEditedApp] = useState<any>();
const [tabActiveKey, setTabActiveKey] = useState('security'); const [tabActiveKey, setTabActiveKey] = useState('security');
const [loginLogData, setLoginLogData] = useState<any[]>([]); const [loginLogData, setLoginLogData] = useState<any[]>([]);
const [notificationInfo, setNotificationInfo] = useState<any>(); const [notificationInfo, setNotificationInfo] = useState<any>();
@ -143,6 +143,7 @@ const Setting = ({
}; };
const addApp = () => { const addApp = () => {
setEditedApp(null);
setIsModalVisible(true); setIsModalVisible(true);
}; };