mirror of
https://github.com/whyour/qinglong.git
synced 2025-07-28 15:36:07 +08:00
Merge branch 'develop' into develop
This commit is contained in:
commit
66d6c2f13c
59
.github/workflows/build_base.yml
vendored
59
.github/workflows/build_base.yml
vendored
|
@ -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
|
|
@ -16,6 +16,10 @@ export default defineConfig({
|
|||
},
|
||||
favicon: '/images/g5.ico',
|
||||
proxy: {
|
||||
'/api/public': {
|
||||
target: 'http://127.0.0.1:5400/',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/api': {
|
||||
target: 'http://127.0.0.1:5600/',
|
||||
changeOrigin: true,
|
||||
|
|
21
README.md
21
README.md
|
@ -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 部署
|
||||
|
||||
1. docker 安装
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
FROM whyour/ql:base
|
||||
FROM node:alpine
|
||||
|
||||
ARG QL_MAINTAINER="whyour"
|
||||
LABEL maintainer="${QL_MAINTAINER}"
|
||||
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}
|
||||
|
||||
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} \
|
||||
&& cp -f .env.example .env \
|
||||
&& chmod 777 ${QL_DIR}/shell/*.sh \
|
||||
&& chmod 777 ${QL_DIR}/docker/*.sh \
|
||||
&& cp -rf /node_modules ./ \
|
||||
&& rm -rf /node_modules \
|
||||
&& pnpm install --prod \
|
||||
&& rm -rf /root/.pnpm-store \
|
||||
&& rm -rf /root/.cache \
|
||||
&& rm -rf /root/.npm \
|
||||
&& 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
|
||||
|
||||
ENTRYPOINT ["./docker/docker-entrypoint.sh"]
|
||||
|
|
|
@ -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" ]
|
|
@ -4,10 +4,12 @@
|
|||
"start": "concurrently -n w: npm:start:*",
|
||||
"start:front": "umi dev",
|
||||
"start:back": "nodemon",
|
||||
"start:public": "ts-node back/public.ts",
|
||||
"build:front": "umi build",
|
||||
"build:back": "tsc -p tsconfig.back.json",
|
||||
"panel": "npm run build:back && node static/build/app.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}'",
|
||||
"prepare": "umi generate tmp",
|
||||
"test": "umi-test",
|
||||
|
|
12
shell/bot.sh
12
shell/bot.sh
|
@ -22,9 +22,9 @@ if [[ ! -d ${repo_path}/.git ]]; then
|
|||
git_clone_scripts ${url} ${repo_path} "main"
|
||||
fi
|
||||
|
||||
cp -rf "$repo_path/jbot" $dir_root
|
||||
if [[ ! -f "$dir_root/config/bot.json" ]]; then
|
||||
cp -f "$repo_path/config/bot.json" "$dir_root/config"
|
||||
cp -rf "$repo_path/jbot" $dir_data
|
||||
if [[ ! -f "$dir_config/bot.json" ]]; then
|
||||
cp -f "$repo_path/config/bot.json" "$dir_config"
|
||||
fi
|
||||
echo -e "\nbot文件下载成功...\n"
|
||||
|
||||
|
@ -32,9 +32,9 @@ echo -e "3、安装python3依赖...\n"
|
|||
if [[ $PipMirror ]]; then
|
||||
pip3 config set global.index-url $PipMirror
|
||||
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
|
||||
do
|
||||
if [[ ! $(pip3 show "${LREAD%%=*}" 2>/dev/null) ]]; then
|
||||
|
@ -46,7 +46,7 @@ echo -e "\npython3依赖安装成功...\n"
|
|||
|
||||
echo -e "4、启动bot程序...\n"
|
||||
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
|
||||
nohup python3 -m jbot >$dir_log/bot/nohup.log 2>&1 &
|
||||
echo -e "bot启动成功...\n"
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
.monaco-editor:not(.rename-box) {
|
||||
height: calc(100vh - 128px) !important;
|
||||
height: calc(100vh - var(--vh-offset, 0px) - 128px) !important;
|
||||
|
||||
.view-overlays .current-line {
|
||||
border-width: 0;
|
||||
}
|
||||
|
@ -33,6 +34,7 @@
|
|||
|
||||
.rename-box {
|
||||
height: 0;
|
||||
|
||||
.rename-input {
|
||||
height: 0;
|
||||
padding: 0 !important;
|
||||
|
@ -43,6 +45,7 @@
|
|||
.ant-pro-grid-content.wide {
|
||||
max-width: unset !important;
|
||||
overflow: auto;
|
||||
|
||||
.ant-pro-page-container-children-content {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
|
@ -57,6 +60,7 @@
|
|||
|
||||
.ant-tooltip {
|
||||
max-width: 500px !important;
|
||||
|
||||
.ant-tooltip-inner {
|
||||
word-break: break-all !important;
|
||||
}
|
||||
|
@ -72,6 +76,7 @@
|
|||
.log-select {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.ant-page-header-heading-left {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
@ -81,6 +86,7 @@
|
|||
.config-select {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.ant-page-header-heading-left {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
@ -119,10 +125,13 @@
|
|||
flex-direction: column;
|
||||
height: calc(100vh - 48px);
|
||||
height: calc(100vh - var(--vh-offset, 0px) - 48px);
|
||||
|
||||
.ant-pro-grid-content.wide {
|
||||
flex: 1;
|
||||
|
||||
.ant-pro-grid-content-children {
|
||||
height: calc(100% - 36px);
|
||||
|
||||
> div,
|
||||
.log-container,
|
||||
.react-codemirror2,
|
||||
|
@ -197,6 +206,7 @@
|
|||
.Resizer.disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.Resizer.disabled:hover {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
@ -209,6 +219,7 @@
|
|||
|
||||
.inline-countdown.ant-statistic {
|
||||
display: inline-block;
|
||||
|
||||
.ant-statistic-content {
|
||||
font-size: 14px;
|
||||
padding: 0 3px;
|
||||
|
@ -253,6 +264,7 @@
|
|||
|
||||
.ant-pro-basicLayout-content {
|
||||
margin: 18px;
|
||||
|
||||
.ant-pro-page-container {
|
||||
margin: -18px -18px -18px;
|
||||
}
|
||||
|
@ -280,25 +292,27 @@
|
|||
|
||||
.side-menu-user-drop-menu {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 4px 0;
|
||||
text-align: left;
|
||||
background-clip: padding-box;
|
||||
outline: none;
|
||||
background-color: #fff;
|
||||
padding: 4px 0;
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 6px 16px -8px rgb(0 0 0 / 8%), 0 9px 28px 0 rgb(0 0 0 / 5%),
|
||||
0 12px 48px 16px rgb(0 0 0 / 3%);
|
||||
.ant-dropdown-menu-item:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
overflow: auto;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[data-dark='true'] .side-menu-user-drop-menu {
|
||||
background-color: #373739;
|
||||
}
|
||||
|
||||
.ant-pro-sider-logo {
|
||||
padding: 16px 8px !important;
|
||||
|
||||
h1 {
|
||||
margin-left: 5px !important;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 32px !important;
|
||||
border-radius: 52% !important;
|
||||
|
|
|
@ -272,7 +272,7 @@ export default function (props: any) {
|
|||
collapsed={collapsed}
|
||||
rightContentRender={() =>
|
||||
ctx.isPhone && (
|
||||
<Dropdown overlay={menu} trigger={['click']}>
|
||||
<Dropdown overlay={menu} placement="bottomRight" trigger={['click']}>
|
||||
<span className="side-menu-user-wrapper">
|
||||
<Avatar shape="square" size="small" icon={<UserOutlined />} />
|
||||
<span style={{ marginLeft: 5 }}>admin</span>
|
||||
|
@ -289,7 +289,7 @@ export default function (props: any) {
|
|||
}}
|
||||
>
|
||||
{!collapsed && !ctx.isPhone && (
|
||||
<Dropdown overlay={menu} trigger={['hover']}>
|
||||
<Dropdown overlay={menu} placement="topLeft" trigger={['hover']}>
|
||||
<span className="side-menu-user-wrapper">
|
||||
<Avatar shape="square" size="small" icon={<UserOutlined />} />
|
||||
<span style={{ marginLeft: 5 }}>admin</span>
|
||||
|
|
|
@ -50,11 +50,13 @@ const CronDetailModal = ({
|
|||
handleCancel,
|
||||
visible,
|
||||
theme,
|
||||
isPhone,
|
||||
}: {
|
||||
cron?: any;
|
||||
visible: boolean;
|
||||
handleCancel: (needUpdate?: boolean) => void;
|
||||
theme: string;
|
||||
isPhone: boolean;
|
||||
}) => {
|
||||
const [activeTabKey, setActiveTabKey] = useState('log');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
@ -64,6 +66,8 @@ const CronDetailModal = ({
|
|||
const [isLogModalVisible, setIsLogModalVisible] = useState(false);
|
||||
const editorRef = useRef<any>(null);
|
||||
const [scriptInfo, setScriptInfo] = useState<any>({});
|
||||
const [logUrl, setLogUrl] = useState('');
|
||||
const [validTabs, setValidTabs] = useState(tabList);
|
||||
|
||||
const contentList: any = {
|
||||
log: (
|
||||
|
@ -99,6 +103,8 @@ const CronDetailModal = ({
|
|||
};
|
||||
|
||||
const onClickItem = (item: LogItem) => {
|
||||
localStorage.setItem('logCron', cron.id);
|
||||
setLogUrl(`${config.apiPrefix}logs/${item.directory}/${item.filename}`);
|
||||
request
|
||||
.get(`${config.apiPrefix}logs/${item.directory}/${item.filename}`)
|
||||
.then((data) => {
|
||||
|
@ -126,6 +132,7 @@ const CronDetailModal = ({
|
|||
const getScript = () => {
|
||||
const cmd = cron.command.split(' ') as string[];
|
||||
if (cmd[0] === 'task') {
|
||||
setValidTabs(validTabs);
|
||||
if (cmd[1].startsWith('/ql/data/scripts')) {
|
||||
cmd[1] = cmd[1].replace('/ql/data/scripts/', '');
|
||||
}
|
||||
|
@ -141,6 +148,8 @@ const CronDetailModal = ({
|
|||
.then((data) => {
|
||||
setValue(data.data);
|
||||
});
|
||||
} else {
|
||||
setValidTabs([validTabs[0]]);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -171,6 +180,7 @@ const CronDetailModal = ({
|
|||
})
|
||||
.then((_data: any) => {
|
||||
if (_data.code === 200) {
|
||||
setValue(content);
|
||||
message.success(`保存成功`);
|
||||
} else {
|
||||
message.error(_data);
|
||||
|
@ -198,7 +208,9 @@ const CronDetailModal = ({
|
|||
title={
|
||||
<>
|
||||
<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[0] !== '' &&
|
||||
cron.labels?.map((label: string, i: number) => (
|
||||
|
@ -213,11 +225,17 @@ const CronDetailModal = ({
|
|||
forceRender
|
||||
footer={false}
|
||||
onCancel={() => handleCancel()}
|
||||
width={'80vw'}
|
||||
bodyStyle={{ background: '#eee', padding: 12 }}
|
||||
wrapClassName="crontab-detail"
|
||||
width={!isPhone ? '80vw' : ''}
|
||||
>
|
||||
<div style={{ height: '80vh' }}>
|
||||
<Card bodyStyle={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||
<div className="card-wrapper">
|
||||
<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-title">状态</div>
|
||||
<div className="cron-detail-info-value">
|
||||
|
@ -250,10 +268,6 @@ const CronDetailModal = ({
|
|||
)}
|
||||
</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-title">定时</div>
|
||||
<div className="cron-detail-info-value">{cron.schedule}</div>
|
||||
|
@ -289,8 +303,8 @@ const CronDetailModal = ({
|
|||
</div>
|
||||
</Card>
|
||||
<Card
|
||||
style={{ marginTop: 16 }}
|
||||
tabList={tabList}
|
||||
style={{ marginTop: 10 }}
|
||||
tabList={validTabs}
|
||||
activeTabKey={activeTabKey}
|
||||
onTabChange={(key) => {
|
||||
onTabChange(key);
|
||||
|
@ -306,7 +320,6 @@ const CronDetailModal = ({
|
|||
</Button>
|
||||
)
|
||||
}
|
||||
bodyStyle={{ height: 'calc(80vh - 188px)', overflowY: 'auto' }}
|
||||
>
|
||||
{contentList[activeTabKey]}
|
||||
</Card>
|
||||
|
@ -318,6 +331,7 @@ const CronDetailModal = ({
|
|||
}}
|
||||
cron={cron}
|
||||
data={log}
|
||||
logUrl={logUrl}
|
||||
/>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -8,15 +8,74 @@
|
|||
}
|
||||
}
|
||||
|
||||
.cron-detail-info-item {
|
||||
flex: auto;
|
||||
.crontab-detail {
|
||||
.card-wrapper {
|
||||
height: 80vh;
|
||||
height: calc(80vh - var(--vh-offset, 0px));
|
||||
|
||||
.cron-detail-info-title {
|
||||
color: #888;
|
||||
.ant-card:last-child {
|
||||
.ant-card-body {
|
||||
height: calc(80vh - 238px);
|
||||
height: calc(80vh - var(--vh-offset, 0px) - 238px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cron-detail-info-value {
|
||||
margin-top: 18px;
|
||||
.ant-modal-body {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ import { getTableScroll } from '@/utils/index';
|
|||
import { history } from 'umi';
|
||||
import './index.less';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Text, Paragraph } = Typography;
|
||||
const { Search } = Input;
|
||||
|
||||
export enum CrontabStatus {
|
||||
|
@ -85,28 +85,32 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
|||
goToScriptManager(record);
|
||||
}}
|
||||
>
|
||||
<Popover
|
||||
placement="right"
|
||||
trigger={isPhone ? 'click' : 'hover'}
|
||||
content={
|
||||
<div>
|
||||
{record.labels?.map((label: string) => (
|
||||
<Tag
|
||||
color="blue"
|
||||
style={{ cursor: 'point' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setSearchText(`label:${label}`);
|
||||
}}
|
||||
>
|
||||
<a>{label}</a>
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{record.name || '-'}
|
||||
</Popover>
|
||||
{record.labels?.length > 0 && record.labels[0] !== '' ? (
|
||||
<Popover
|
||||
placement="right"
|
||||
trigger={isPhone ? 'click' : 'hover'}
|
||||
content={
|
||||
<div>
|
||||
{record.labels?.map((label: string) => (
|
||||
<Tag
|
||||
color="blue"
|
||||
style={{ cursor: 'point' }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setSearchValue(`label:${label}`);
|
||||
}}
|
||||
>
|
||||
<a>{label}</a>
|
||||
</Tag>
|
||||
))}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{record.name || '-'}
|
||||
</Popover>
|
||||
) : (
|
||||
record.name || '-'
|
||||
)}
|
||||
{record.isPinned ? (
|
||||
<span>
|
||||
<PushpinOutlined />
|
||||
|
@ -130,16 +134,16 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
|||
align: 'center' as const,
|
||||
render: (text: string, record: any) => {
|
||||
return (
|
||||
<span
|
||||
<Paragraph
|
||||
style={{
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
display: 'inline-block',
|
||||
wordBreak: 'break-all',
|
||||
marginBottom: 0,
|
||||
textAlign: 'left',
|
||||
}}
|
||||
ellipsis={{ tooltip: text, rows: 2 }}
|
||||
>
|
||||
{text}
|
||||
</span>
|
||||
</Paragraph>
|
||||
);
|
||||
},
|
||||
sorter: {
|
||||
|
@ -364,6 +368,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
|||
const [tableScrollHeight, setTableScrollHeight] = useState<number>();
|
||||
const [isDetailModalVisible, setIsDetailModalVisible] = useState(false);
|
||||
const [detailCron, setDetailCron] = useState<any>();
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
|
||||
const goToScriptManager = (record: any) => {
|
||||
const cmd = record.command.split(' ') as string[];
|
||||
|
@ -627,7 +632,8 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
|||
index: number;
|
||||
}> = ({ record, index }) => (
|
||||
<Dropdown
|
||||
arrow
|
||||
arrow={{ pointAtCenter: true }}
|
||||
placement="bottomRight"
|
||||
trigger={['click']}
|
||||
overlay={
|
||||
<Menu
|
||||
|
@ -842,7 +848,8 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
|||
enterButton
|
||||
allowClear
|
||||
loading={loading}
|
||||
value={searchText}
|
||||
value={searchValue}
|
||||
onChange={(e) => setSearchValue(e.target.value)}
|
||||
onSearch={onSearch}
|
||||
/>,
|
||||
<Button key="2" type="primary" onClick={() => addCron()}>
|
||||
|
@ -968,6 +975,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
|
|||
}}
|
||||
cron={detailCron}
|
||||
theme={theme}
|
||||
isPhone={isPhone}
|
||||
/>
|
||||
</PageContainer>
|
||||
);
|
||||
|
|
|
@ -21,11 +21,13 @@ const CronLogModal = ({
|
|||
handleCancel,
|
||||
visible,
|
||||
data,
|
||||
logUrl,
|
||||
}: {
|
||||
cron?: any;
|
||||
visible: boolean;
|
||||
handleCancel: () => void;
|
||||
data?: string;
|
||||
logUrl?: string;
|
||||
}) => {
|
||||
const [value, setValue] = useState<string>('启动中...');
|
||||
const [loading, setLoading] = useState<any>(true);
|
||||
|
@ -38,7 +40,7 @@ const CronLogModal = ({
|
|||
setLoading(true);
|
||||
}
|
||||
request
|
||||
.get(`${config.apiPrefix}crons/${cron.id}/log`)
|
||||
.get(logUrl ? logUrl : `${config.apiPrefix}crons/${cron.id}/log`)
|
||||
.then((data: any) => {
|
||||
if (localStorage.getItem('logCron') === String(cron.id)) {
|
||||
const log = data.data as string;
|
||||
|
@ -99,10 +101,10 @@ const CronLogModal = ({
|
|||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (cron && cron.id) {
|
||||
if (cron && cron.id && visible) {
|
||||
getCronLog(true);
|
||||
}
|
||||
}, [cron]);
|
||||
}, [cron, visible]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data) {
|
||||
|
|
20
src/pages/env/index.tsx
vendored
20
src/pages/env/index.tsx
vendored
|
@ -32,7 +32,7 @@ import './index.less';
|
|||
import { getTableScroll } from '@/utils/index';
|
||||
import { doc } from 'prettier';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Text, Paragraph } = Typography;
|
||||
const { Search, TextArea } = Input;
|
||||
|
||||
enum Status {
|
||||
|
@ -124,18 +124,18 @@ const Env = ({ headerStyle, isPhone, theme }: any) => {
|
|||
key: 'value',
|
||||
align: 'center' as const,
|
||||
width: '35%',
|
||||
ellipsis: {
|
||||
showTitle: false,
|
||||
},
|
||||
render: (text: string, record: any) => {
|
||||
return (
|
||||
<Tooltip
|
||||
placement="topLeft"
|
||||
title={text}
|
||||
trigger={['hover', 'click']}
|
||||
<Paragraph
|
||||
style={{
|
||||
wordBreak: 'break-all',
|
||||
marginBottom: 0,
|
||||
textAlign: 'left',
|
||||
}}
|
||||
ellipsis={{ tooltip: text, rows: 2 }}
|
||||
>
|
||||
<span>{text}</span>
|
||||
</Tooltip>
|
||||
{text}
|
||||
</Paragraph>
|
||||
);
|
||||
},
|
||||
},
|
||||
|
|
|
@ -177,7 +177,7 @@ const CheckUpdate = ({ socketMessage }: any) => {
|
|||
秒后自动刷新
|
||||
</span>
|
||||
),
|
||||
duration: 10,
|
||||
duration: 30,
|
||||
});
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
|
|
|
@ -120,7 +120,7 @@ const Setting = ({
|
|||
const [theme, setTheme] = useState(defaultDarken);
|
||||
const [dataSource, setDataSource] = useState<any[]>([]);
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [editedApp, setEditedApp] = useState();
|
||||
const [editedApp, setEditedApp] = useState<any>();
|
||||
const [tabActiveKey, setTabActiveKey] = useState('security');
|
||||
const [loginLogData, setLoginLogData] = useState<any[]>([]);
|
||||
const [notificationInfo, setNotificationInfo] = useState<any>();
|
||||
|
@ -143,6 +143,7 @@ const Setting = ({
|
|||
};
|
||||
|
||||
const addApp = () => {
|
||||
setEditedApp(null);
|
||||
setIsModalVisible(true);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user